From 2d05081e15df2acb932da57a4ca395d7d1cd412f Mon Sep 17 00:00:00 2001 From: Nickolay Date: Mon, 22 Jul 2024 17:25:37 +1200 Subject: [PATCH 1/2] Update README.md --- README.md | 791 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 779 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 6c6a16c..0c99028 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,809 @@ # UNIGRAM PAYMENT -[![License](https://img.shields.io/github/license/mrveit/veittech-unigrampayment?color=318CE7&style=flat-square)](LICENSE) -[![Version](https://img.shields.io/github/package-json/v/mrveit/veittech-unigrampayment?color=318CE7&style=flat-square)](package.json) +[![License](https://img.shields.io/github/license/MrVeit/Veittech-UnigramPayment?color=318CE7&style=flat-square)](LICENSE) +[![Version](https://img.shields.io/github/package-json/v/MrVeit/Veittech-UnigramPayment?color=318CE7&style=flat-square)](package.json) [![Unity](https://img.shields.io/badge/Unity-2020.1+-2296F3.svg?color=318CE7&style=flat-square)](https://unity.com/releases/editor/archive) +

+ qr +

+ +**UNIGRAM PAYMENT** is a library for making payments inside web applications made on Unity using a new internal currency - Telegram Stars. No need to connect payment providers as it was before. + # Technical Demo -You can test the SDK without installation on the demo app [in Telegram bot](https://t.me/UnigramPayment_bot/launch). +You can test the SDK without installation in the TMA (Telegram Mini App) demo [via Telegram bot](https://t.me/UnigramPayment_bot/launch). # Dependencies -For the library to work correctly, the following dependencies **must be installed** in the project before use: +Install the following plugins/libraries for the SDK to work correctly: + - **[Newtonsoft](https://www.youtube.com/watch?v=3H6xkl_EsvQ)** - modern solution for convenient work with json files. # Installation -[Download the latest version of the SDK via the .unityPackage file here](https://github.com/MrVeit/Veittech-UnigramPayment/releases). +**[Download the latest version of the SDK via the .unityPackage file here](https://github.com/MrVeit/Veittech-UnigramPayment/releases).** # Initialization -Before you can use all the features of the SDK, you must initialize it in **one of two available ways**. +### Initializing backend components + +Before you start testing the library in your project, you need to set up the base, without which it will not work. +To do this, it is necessary to run locally `Server API` and `Telegram Bot`, in which the logic of payments will be conducte. + +To do this, you need to make a clone of the Server API repository that is written in Node.js: +``` +https://github.com/MrVeit/Veittech-UnigramPayment-ServerAPI +``` +And also a clone of the Telegram Bot repository, which is also written in Node.js: +``` +https://github.com/MrVeit/Veittech-UnigramPayment-TelegramBot +``` + +If you already have Node.js installed on your PC `running Windows` (does anyone make Unity games on Linux? :D), you can skip this step and move on to the next one. If you still don't have it installed, you need to go to the official Node.js website and [install it yourself](https://nodejs.org/en/). + +After installing and cloning the above two repositories, you can open both projects in `VS Code` or `any other code editor` that **supports Node.js**. + +Now, to be able to run these projects locally and start testing, you need to create a repository environment variable. To do this, you need to create a file named `.env` but **without a file format** in the directory of both repositories. + +For the API Server repository environment variable, you need to fill in the following information: + +```config +SERVER_DOMAIN = http://localhost + +BOT_TOKEN = YOUR_BOT_TOKEN +BOT_SECRET_KEY = “test_app_unigram_payment” + +CLIENT_SECRET_KEY = “test_unity_unigram” +CLIENT_JWT_SIGN = “unigram_payment-unity” +``` + +- The `SERVER_DOMAIN` variable is a link to your domain where the API server is running, for the purposes of testing we will use the local address `http://localhost`, + +- The `BOT_TOKEN` variable is the token of your Telegram bot, which can be created in [Bot Father](https://t.me/BotFather) inside Telegram by following the following simple steps. After creating the bot, you need to enter the command in **`/mybots`** and then go to the following path: `Select your bot -> Go to API Token -> Copy the value from there -> Paste the variable value without quotes`, + +- The `BOT_SECRET_KEY` variable is the signature key by which the API server will identify your bot when it receives a check for payment. Ignoring third-party requests if they do not match this value, after decrypting the bot token. +Here you can use a pair of two words or one with numbers, using any special characters. +**IMPORTANT:** Store this key securely, it should not be public, + +- The `CLIENT_SECRET_KEY` variable is the same signature key as the previous one, but for your Unity game. With it, before creating a payment request, you will need to authorize on the API server. **IMPORTANT:** It must also be stored securely and must not be public, + +- The `CLIENT_JWT_SIGN` variable is an additional signing key with which your Unity game, after authorizing to the API server and receiving a generated JWT token, is signed with this key. Then, when requesting other API methods, the Unity game sends this generated JWT token in the Authorizatio header, and the server decrypts the token value with this key to allow access to its functionality if the values match. + +For the Telegram bot, the environment variables will look like this: +```config +BOT_TOKEN = YOUR_BOT_TOKEN +SERVER_DOMAIN = http://localhost:1000 + +AUTHORIZATION_SECRET_KEY = “test_app_unigram_payment” +``` + +- The `BOT_TOKEN` variable should contain the bot token that you have previously filled in and inserted into a variable of the same name for the server API. +- The `SERVER_DOMAIN` variable is a reference to your API server. For testing purposes, leave the specified value unchanged. +- The `AUTHORIZATION_SECRET_KEY` variable should contain the same value that you filled in for `BOT_SECRET_KEY` for the API server. + +Now you can run both projects to start testing. +Open the terminal in the code editor in which you opened these projects and enter the following commands: + +To activate the API server, enter the command: +``` +node server.js +``` + +To activate the Telegram bot, enter the command: +``` +npm start +``` + +**IMPORTANT:** In case you encounter a startup problem at this stage, it means you don't have Node.js installed or it was installed incorrectly. +Try reinstalling or searching for a solution to your problem on the Internet. + +### Initializing the Unity Client + +Once the necessary backend components have been successfully installed and running, you can start customizing your Unity project. + +#### Automatic Initialization +The `UnigramPaymentSDK` component has an option `Initialize On Awake`. When it is activated, the SDK is initialized automatically. You will only have to subscribe to the necessary events and start working with it. + +

+ qr +

+ +#### Manual Initialization +Below is a test example of what this might look like: + +```c# +public sealed class UsageTemplate : MonoBehaviour +{ + private UnigramPaymentSDK _unigramPayment; + + private void OnDisable() + { + _unigramPayment.OnInitialized -= UnigramPaymentInitialized; + } + + private void Start() + { + _unigramPayment = UnigramPaymentSDK.Instance; + + _unigramPayment.OnInitialized += UnigramPaymentInitialized; + + _unigramPayment.Initialize(); + } + + private void UnigramPaymentInitialized(bool isSuccess) + { + if (isSuccess) + { + Debug.Log("Success initialize Unigram Payment SDK"); + } + } +} +``` + +### Possible problems: + +After writing a script to initialize the SDK. You may encounter a number of errors because the configuration of the connection to the test API server is not yet set up. + +So you need to go to the configuration window via `Unigram Payment -> API Config`. +Now you need to fill the `Client Secret Key` field with the value you previously entered for the API server variable `CLIENT_SECRET_KEY`. +You can leave the `Server Url` field unchanged if you want to do local testing. + +

+ qr +

+ +# Usage Template + +Now it's time to look at examples of using the Unigram Payment `library API`. +After successful initialization, you can create a test invoice for payment. + +**IMPORTANT:** the library makes a special storage with products in the form of Scriptable Object, the configuration of which contains such fields as: `Id`, `Name`, `Description` and its `Price` in Telegram Stars. + +You can find this storage by going to `Assets -> Unigram Payment -> Items Storage`. To add your own items, right click on the project window and go to `Create -> Unigram Payment -> Saleable Item`. + +### Creating a payment invoice + +Below you can see an example of creating an invoice to pay for an item in Telegram Stars: + +```c# +public sealed class UsageTemplate : MonoBehaviour +{ + [SerializeField, Space] private Button _createInvoiceButton; + [SerializeField, Space] private SaleableItemsStorage _itemsStorage; + + private UnigramPaymentSDK _unigramPayment; + + private string _latestInvoice; + + private void OnDisable() + { + _createInvoiceButton.onClick.RemoveListener(CreateInvoice); + + _unigramPayment.OnInitialized -= UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated -= PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed -= PaymentInvoiceCreateFailed; + } + + private void Start() + { + _createInvoiceButton.onClick.AddListener(CreateInvoice); + + _unigramPayment = UnigramPaymentSDK.Instance; + + _unigramPayment.OnInitialized += UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated += PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed += PaymentInvoiceCreateFailed; + + _unigramPayment.Initialize(); + } + + private void CreateInvoice() + { + var randomItemFromStorage = _itemsStorage.Items[Random.Range(0, _itemsStorage.Items.Count - 1)]; + + Debug.Log($"Claimed item with payload id: {randomItemFromStorage.Id}"); + + _unigramPayment.CreateInvoice(randomItemFromStorage); + } + + private void UnigramPaymentInitialized(bool isSuccess) + { + if (isSuccess) + { + Debug.Log("Success initialize Unigram Payment SDK"); + } + } + + private void PaymentInvoiceCreated(string invoiceLink) + { + _latestInvoice = invoiceLink; + + Debug.Log("The link to purchase the test item has been successfully generated: {url}"); + } + + private void PaymentInvoiceCreateFailed() + { + Debug.LogError("Failed to create a payment link for one of the following reasons"); + } +} +``` + +Now you will easily get a payment link, which you can open in your browser and pay in `your Telegram bot` if it was launched locally. + +**IMPORTANT:** Processing a callback with receipt of payment check and subsequent refund **NOT AVAILABLE IN EDITOR**. So you need to create an assembly for WebGL and upload it to `Github Pages` or anywhere else where you have an `HTTPS Connection` and a valid `SSL Certificate` (I won't describe a detailed tutorial here, as you can find that online). + +**P.S:** for detailed information on how to properly build a project with the Unigram Payment library, go to the [`Build`](https://github.com/MrVeit/Veittech-UnigramPayment#build) section. + +### Invoice opening and payment + +The following shows the implementation of opening and paying an invoice. The result is processed through appropriate callbacks from receipt of the check when payment is successful or unsuccessful: + +```c# +public sealed class UsageTemplate : MonoBehaviour +{ + [SerializeField, Space] private Button _createInvoice; + [SerializeField] private Button _openInvoice; + [SerializeField, Space] private SaleableItemsStorage _itemsStorage; + + private UnigramPaymentSDK _unigramPayment; + + private PaymentReceiptData _itemPaymentReceipt; + + private string _latestInvoice; + + private void OnDisable() + { + _createInvoice.onClick.RemoveListener(CreateInvoice); + _openInvoice.onClick.RemoveListener(OpenInvoice); + + _unigramPayment.OnInitialized -= UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated -= PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed -= PaymentInvoiceCreateFailed; + + _unigramPayment.OnItemPurchased -= ItemPurchased; + _unigramPayment.OnItemPurchaseFailed -= ItemPurchaseFailed; + } + + private void Start() + { + _createInvoice.onClick.AddListener(CreateInvoice); + _openInvoice.onClick.AddListener(OpenInvoice); + + _unigramPayment = UnigramPaymentSDK.Instance; + + _unigramPayment.OnInitialized += UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated += PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed += PaymentInvoiceCreateFailed; + + _unigramPayment.OnItemPurchased += ItemPurchased; + _unigramPayment.OnItemPurchaseFailed += ItemPurchaseFailed; + + _unigramPayment.Initialize(); + } + + private void CreateInvoice() + { + var randomItemFromStorage = _itemsStorage.Items[Random.Range(0, _itemsStorage.Items.Count - 1)]; + + Debug.Log($"Claimed item with payload id: {randomItemFromStorage.Id}"); + + _unigramPayment.CreateInvoice(randomItemFromStorage); + } + + private void OpenInvoice() + { + _unigramPayment.OpenInvoice(_latestInvoice); + } + + private void UnigramPaymentInitialized(bool isSuccess) + { + if (isSuccess) + { + Debug.Log("Success initialize Unigram Payment SDK"); + } + } + + private void PaymentInvoiceCreated(string invoiceLink) + { + _latestInvoice = invoiceLink; + + Debug.Log($"The link to purchase the test item has been successfully generated: {url}"); + } + + private void PaymentInvoiceCreateFailed() + { + Debug.LogError("Failed to create a payment link for one of the following reasons"); + } + + private void ItemPurchased(PaymentReceiptData receipt) + { + _itemPaymentReceipt = receipt; + + Debug.Log($"The item with identifier {_itemPaymentReceipt.InvoicePayload} " + + $"was successfully purchased for {_itemPaymentReceipt.Amount} " + + $"stars by the buyer with telegram id {_itemPaymentReceipt.BuyerId}"); + } + + private void ItemPurchaseFailed() + { + Debug.LogError("Failed to purchase an item for one of the following reasons"); + } +} +``` + +When called to open a **previously generated invoice**, you will be presented with a native Pop up window to make a payment, which you can close without payment or pay - the results of both cases will be processed by the SDK. + +### Payment refund + +The following shows the implementation of a call to return a previously paid invoice: + +```c# +public sealed class UsageTemplate : MonoBehaviour +{ + [SerializeField, Space] private Button _createInvoice; + [SerializeField] private Button _openInvoice; + [SerializeField] private Button _refundPayment; + [SerializeField, Space] private SaleableItemsStorage _itemsStorage; + + private UnigramPaymentSDK _unigramPayment; + + private PaymentReceiptData _itemPaymentReceipt; + + private string _latestInvoice; + + private void OnDisable() + { + _createInvoice.onClick.RemoveListener(CreateInvoice); + _openInvoice.onClick.RemoveListener(OpenInvoice); + _refundPayment.onClick.RemoveListener(Refund); + + _unigramPayment.OnInitialized -= UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated -= PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed -= PaymentInvoiceCreateFailed; + + _unigramPayment.OnItemPurchased -= ItemPurchased; + _unigramPayment.OnItemPurchaseFailed -= ItemPurchaseFailed; + + _unigramPayment.OnRefundTransactionFinished -= RefundTransactionFinished; + } + + private void Start() + { + _createInvoice.onClick.AddListener(CreateInvoice); + _openInvoice.onClick.AddListener(OpenInvoice); + _refundPayment.onClick.AddListener(Refund); + + _unigramPayment = UnigramPaymentSDK.Instance; + + _unigramPayment.OnInitialized += UnigramPaymentInitialized; + + _unigramPayment.OnInvoiceLinkCreated += PaymentInvoiceCreated; + _unigramPayment.OnInvoiceLinkCreateFailed += PaymentInvoiceCreateFailed; + + _unigramPayment.OnItemPurchased += ItemPurchased; + _unigramPayment.OnItemPurchaseFailed += ItemPurchaseFailed; + + _unigramPayment.OnRefundTransactionFinished += RefundTransactionFinished; + + _unigramPayment.Initialize(); + } + + private void CreateInvoice() + { + var randomItemFromStorage = _itemsStorage.Items[Random.Range(0, _itemsStorage.Items.Count - 1)]; + + Debug.Log($"Claimed item with payload id: {randomItemFromStorage.Id}"); + + _unigramPayment.CreateInvoice(randomItemFromStorage); + } + + private void OpenInvoice() + { + _unigramPayment.OpenInvoice(_latestInvoice); + } + + private void Refund() + { + _unigramPayment.Refund(_itemPaymentReceipt); + } + + private void UnigramPaymentInitialized(bool isSuccess) + { + if (isSuccess) + { + Debug.Log("Success initialize Unigram Payment SDK"); + } + } + + private void PaymentInvoiceCreated(string invoiceLink) + { + _latestInvoice = invoiceLink; + + Debug.Log($"The link to purchase the test item has been successfully generated: {url}"); + } + + private void PaymentInvoiceCreateFailed() + { + Debug.LogError("Failed to create a payment link for one of the following reasons"); + } + + private void ItemPurchased(PaymentReceiptData receipt) + { + _itemPaymentReceipt = receipt; + + Debug.Log($"The item with identifier {_itemPaymentReceipt.InvoicePayload} " + + $"was successfully purchased for {_itemPaymentReceipt.Amount} " + + $"stars by the buyer with telegram id {_itemPaymentReceipt.BuyerId}"); + } + + private void ItemPurchaseFailed() + { + Debug.LogError("Failed to purchase an item for one of the following reasons"); + } + + private void RefundTransactionFinished(bool isSuccess) + { + if (isSuccess) + { + Debug.Log("The process of refunding the purchased stars through the transaction with" + + $" the identifier `{_unigramPayment.LastRefundedTransaction}` " + + $"has been completed successfully"); + } + } +} +``` + +After you request a payment refund, the API server contacts the Telegram API for the specified `transaction id` and `buyer id`. The next step is to check if this payment from this user has been in your Telegram bot at all or if it has been previously refunded. After receiving the result, you can display some notification to the user about successful or unsuccessful refund. + +### Access token update + +The API server access token has an expiration date, which you can change at your discretion in the `session.js` script on the server **(by default it is valid for an hour)**. + +After this expires, access to the API for your Unity client is closed, and you need to upgrade. The SDK provides an **automatic token update** if a failed request to the server is made with the corresponding error **Unauthorized client, access denied**. + +If you want to manually refresh the access token, then call the `UnigramPaymentSDK.Instance.RefreshToken()` method and subscribe to the successful refresh result `UnigramPaymentSDK.Instance.OnSessionTokenRefreshed`. + +# Build + +Before you start building your unity project in WebGl, you need to do a few things to get the library **working properly.** + +Go to the `Build Settings` window, then open `Project Settings -> Player -> Resolution and Presentation` and select the `Unigram Pay` build template. To display correctly in Telegram Web View, you need to set `Default Canvas Width` to 1080 and `Default Canvas Height` to 1920, and disable the `Run in Background` option. + +

+ qr +

+ +These are all the necessary steps that need to be done for the project **to build successfully** and for the library functions **to work properly.** + +# Production Backend Deploy + +Here is a **step-by-step guide** to deploying an `API server` and a `Telegram bot` on your server. + +As an example, we will use a `virtual server` on `OC Ubuntu` that was rented from this hosting provider (if you decide to rent a server from them too, go to [this link and get a sweet discount](https://aeza.net/?ref=482600)). + +## Installation of required modules + +Once we have rented a virtual server and connected to it via SSH, we can now install the required modules. -#### Аutomatic initialization: -The `UnigramPaymentSDK` component has an option called `Initialize On Awake`. When you activate it, the SDK will initialize automatically. You only need to subscribe to the necessary events and start working with it. +### Installing Git -#### Manual initialization: -Below is a test example of how it can look like. +Allows you to clone public/private repositories to your server. + +1. Update the list of packages on the server: +``` +sudo apt update +``` + +2. Installing a module: +``` +sudo apt install git -y +``` + +3. After the installation is complete, enter this command to verify that the installation was successful: +``` +git --version +``` + +### Installing Docker + +Runs your project as an isolated container and greatly speeds up the process of running it on the server, without having to write a bunch of commands. + +1. First, you need to download the base packages for Docker: +``` +sudo apt install apt-transport-https ca-certificates curl software-properties-common -y +``` + +2. Now we need to add the official Docker GPG key: +``` +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +``` + +3. Now we need to add the docker repository to APT: +``` +echo “deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable” | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +``` + +4. Update the list of packages on the server: +``` +sudo apt update +``` + +5. Check that Docker will be installed from the official repository with the current version: +``` +apt-cache policy docker-ce +``` + +6. The long-awaited installation of Docker on the server: +``` +sudo apt install docker-ce -y +``` + +### Installing Make + +Allows you to quickly deploy/stop/delete your projects on the server, without having to type a bunch of commands. + +1. Update the list of packages on the server: +``` +sudo apt update +``` + +2. Installing a package: +``` +sudo apt install build-essential -y +``` + +3. Check that the module has been installed correctly: +``` +make --version +``` + +### Installing Nginx + +The web server, which in this example will `proxy all requests` to your API server when you send `GET` and `POST` requests to your server's IP address or domain. + +1. Update the list of packages on the server: +``` +sudo apt update +``` + +2. Installing a package: +``` +sudo apt install nginx -y +``` + +3. Now it is necessary to check the status of Nginx operation: +``` +sudo systemctl status nginx +``` + +4. Go to the IP address of your server in your browser: +``` +http://YOUR_SERVER_IP_ADDRESS +``` + +After the correct installation, you need to make sure that `port 80` is open on the server. +If Nginx is successfully installed and working correctly, you will **see a welcome page.** + +## Configuring components + +### Configuring HTTPS connection + +Telegram has very strict rules regarding requests to third-party resources from Telegram bots. Therefore, it is necessary to add the ability to connect to the API server via HTTPS + +**P.S:** In general, this is a very important point, because with HTTP connection data can be quietly listened to by third parties, which entails information leakage and danger for your users). + +To proceed, you need to **register a domain** for your server so that you can install the certificate there. + +You can do this [right here](https://my.aeza.net/order/domain) if you have already clicked the link and received a [nice discount at checkout](https://aeza.net/?ref=482600). + +Once you have connected your server's IP address in the domain settings, you can start setting up the connection. +If you don't have a reliable SSL certificate, you can generate one with a few commands. + +1. Update the list of packages on the server: +``` +sudo apt update +``` + +2. Install the package and plugin for Nginx: +``` +sudo apt install certbot python3-certbot-nginx -y +``` + +3. Obtaining SSL certificate: +``` +sudo certbot --nginx -d YOUR_DOMAIN_NAME +``` + +In the `YOUR_DOMAIN_NAME` field, write the address of the leased domain that you should have previously purchased at this point. + +Follow the on-screen instructions to complete the process, many of which you can skip by pressing the `Enter` button. +Certbot will prompt you to choose whether to redirect `HTTP` traffic to `HTTPS`, which is recommended for security. + +### Configuring API server configuration + +1. Now you need to go to the nginx configuration directory and create a configuration for your API server: +``` +sudo nano /etc/nginx/sites-available/ +``` + +2. Create a configuration with the name of your domain: +``` +nano YOUR_DOMAIN_NAME +``` + +3. Copy and paste this configuration data, but replace `YOUR_DOMAIN_NAME` with your domain: + +```nginx +server +{ + listen 443 ssl; + server_name YOUR_DOMAIN_NAME; + + ssl_certificate /etc/letsencrypt/live/benizon.shop/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/benizon.shop/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot + + location / { + proxy_pass http://localhost:1000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + add_header 'Access-Control-Allow-Origin' '*' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'Origin, Content-Type, Accept, Authorization' always; + + if ($request_method = OPTIONS) { + return 204; + } + + add_header 'X-Content-Type-Options' 'nosniff'; + add_header 'Server' 'nginx'; + add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; + } +} + +server +{ + listen 80; + server_name YOUR_DOMAIN_NAME; + + if ($host = YOUR_DOMAIN_NAME) { + return 301 https://$host$request_uri; # managed by Certbot + } + + location / { + return 301 https://$host$request_uri; + } +} +``` + +Also, in addition to setting up nginx, in this configuration we set up mandatory headers so that the connection succeeds and is not blocked. + +**IMPORTANT:** Failure to do so will result in an attempt to send `any GET/POST` request from your Web App **inside Telegram being blocked.** + +4. Create a symbolic link to your configuration in the sites-enabled directory: +``` +sudo ln -s /etc/nginx/sites-available/YOUR_DOMAIN_NAME /etc/nginx/sites-enabled/ +``` + +5. Check the Nginx configuration for syntax errors: +``` +sudo nginx -t +``` + +**IMPORTANT:** In case you see `syntax is OK`, you can proceed to the next step. + +6. Reboot Nginx to apply the new configuration: +``` +sudo systemctl reload nginx +``` + +## Deploy + +### Deploy API Server + +After installing all the necessary modules and setting up the necessary configurations, you can start to deploy the API server. + +1. Go to the main directory of the server: +``` +cd /root/ +``` + +2. Clone the repository with the API server into the root directory: +``` +git clone https://github.com/MrVeit/Veittech-UnigramPayment-ServerAPI.git +``` + +3. Get the contents of the directory: +``` +ls -l +``` + +4. Navigate to the server API files: +``` +cd Veittech-UnigramPayment-ServerAPI/src +``` + +5. Now we need to create a file with the storage of environment variables that are used in the project: +``` +nano .env +``` + +We have already created it earlier [at this stage](https://github.com/MrVeit/Veittech-UnigramPayment?tab=readme-ov-file#initializing-backend-components), so copy and paste these values: +The value of the `SERVER_DOMAIN` variable replace it with the domain of your server that you previously rented, binding it to the server. + +6. Start building and creating a docker container with an API server with a single command: +``` +make run +``` + +7. To verify that the docker container is running, enter the following command: +``` +docker ps +``` + +**P.S:** If the container is successfully started, you will see its name in the list, its id, and the time since it was started. + +8. After the deploy, check the docker container logs to make sure no errors occurred: +``` +make logs +``` + +In case the launch **was successful**, you will see only 1 line in the logs: `API Server running at YOUR_DOMAIN_NAME`. +Your API server is now running on the server and ready to accept requests from your client on Unity. + +### Deploy Telegram Bot + +Once the basic set of modules has been installed, you can start running one of the projects. + +1. Navigate to the main directory of the server: +``` +cd /root/ +``` + +2. Clone the repository with the telegram bot into the root directory of the server: +``` +git clone https://github.com/MrVeit/Veittech-UnigramPayment-TelegramBot.git +``` + +3. Get the contents of the directory: +``` +ls -l +``` + +4. Navigate to the Telegram Bot files: +``` +cd Veittech-UnigramPayment-TelegramBot/src +``` + +**IMPORTANT:** Repeat the same steps starting from step 5 that you did to deploy your API server, they are **exactly the same.** + +After you have launched the second container with your Telegram bot, you can go to it and check its functionality. # Donations -If you want to support my work you can send Toncoins to this address: +Ton Wallet (TON/NOT/USDt): ``` UQDPwEk-cnQXEfFaaNVXywpbKACUMwVRupkgWjhr_f4Ursw6 ``` +Multichain Wallet (BTC/ETH/BNB/MATIC) +``` +0x231803Df809C207FaA330646BB5547fD087FEcA1 +``` + **Thanks for your support!** # Support [![Email](https://img.shields.io/badge/-gmail-090909?style=for-the-badge&logo=gmail)](https://mail.google.com/mail/?view=cm&fs=1&to=misster.veit@gmail.com) -[![Telegram](https://img.shields.io/badge/-Telegram-090909?style=for-the-badge&logo=telegram)](https://t.me/MrVeit) +[![Telegram](https://img.shields.io/badge/-Telegram-090909?style=for-the-badge&logo=telegram)](https://t.me/unigram_tools) From b27a28ea5303412539f7d0cdaa3de1348d04d20e Mon Sep 17 00:00:00 2001 From: Mr_Veit Date: Mon, 29 Jul 2024 17:31:32 +1200 Subject: [PATCH 2/2] Added additional `string itemPayloadId` argument when `OnInvoiceLinkCreated` event is triggered and more Added additional argument `string itemPayloadId` when `OnInvoiceLinkCreateFailed` event is triggered. Added additional argument `SaleableItem failedPurchaseItem` when `OnItemPurchaseFailed` event is triggered. Added additional argument `string transactionId` when `OnRefundTransactionFinished` event is triggered. [Bug Fixes] Fixed a bug that caused duplicate invocation of the `OnRefundTransactionFinished` event when a customer purchased more than once in a short period of time. --- .gitignore | 2 ++ .../Samples/TestUnigramPaymentTemplate.cs | 21 +++++++------ .../Samples/UnigramPayment.Demo.unitypackage | Bin 8481 -> 8462 bytes .../IUnigramPaymentTransactionCallbacks.cs | 10 ++++--- .../Runtime/Core/Bridge/WebAppAPIBridge.cs | 18 +++++++++-- .../Source/Runtime/Core/UnigramPaymentSDK.cs | 28 +++++++++++------- 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 3f1f728..e192a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ README.md.meta /Unigram Payment/Editor Resources/Internal Storage TextMesh Pro.meta Resources.meta +/Assets +Assets.meta diff --git a/Unigram Payment/Samples/TestUnigramPaymentTemplate.cs b/Unigram Payment/Samples/TestUnigramPaymentTemplate.cs index bd30e11..f4b7931 100644 --- a/Unigram Payment/Samples/TestUnigramPaymentTemplate.cs +++ b/Unigram Payment/Samples/TestUnigramPaymentTemplate.cs @@ -115,19 +115,21 @@ private void SessionTokenRefreshed() SetInteractableStateByButton(_refundStarsButton, false); } - private void PaymentInvoiceCreated(string url) + private void PaymentInvoiceCreated(string itemPayloadId, string url) { _latestInvoiceLink = url; - _debugBar.text = $"{DEBUG_PREFIX} The link to purchase the test item has been successfully generated: {url}"; + _debugBar.text = $"{DEBUG_PREFIX} The link to purchase the" + + $" test item {itemPayloadId} has been successfully generated: {url}"; SetInteractableStateByButton(_createInvoiceButton, true); } - private void PaymentInvoiceCreateFailed() + private void PaymentInvoiceCreateFailed(string itemPayloadId) { - _debugBar.text = $"{DEBUG_PREFIX} Failed to create a payment link for one of the following reasons:" + - " SDK is not initialized, API server is not started, incorrectly filled in product data."; + _debugBar.text = $"{DEBUG_PREFIX} Failed to create a payment link for" + + $" item {itemPayloadId} for one of the following reasons:" + + $" SDK is not initialized, API server is not started, incorrectly filled in product data."; } private void TargetItemPurchased(PaymentReceiptData receipt) @@ -142,13 +144,14 @@ private void TargetItemPurchased(PaymentReceiptData receipt) SetInteractableStateByButton(_refundStarsButton, true); } - private void TargetItemPurchaseFailed() + private void TargetItemPurchaseFailed(SaleableItem failedPurchaseItem) { - _debugBar.text = $"{DEBUG_PREFIX} Failed to purchase an item for one of the following reasons: " + - "SDK not initialized, API server not configured, or incorrect item data entered."; + _debugBar.text = $"{DEBUG_PREFIX} Failed to purchase an item {failedPurchaseItem.Name}" + + $" for one of the following reasons: SDK not initialized," + + $" API server not configured, or incorrect item data entered."; } - private void RefundTransactionFinished(bool isSuccess) + private void RefundTransactionFinished(string transactionId, bool isSuccess) { if (isSuccess) { diff --git a/Unigram Payment/Samples/UnigramPayment.Demo.unitypackage b/Unigram Payment/Samples/UnigramPayment.Demo.unitypackage index d19144ae5c7ad4d0840438979384e187e7bc9796..5fbf9a88ecc971e1729ed387fd665537916fed8e 100644 GIT binary patch literal 8462 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 onInvoiceSuccessfullyPaid, Action onInvoicePayFailed) { - _onInvoiceSuccessfullyPaid = onInvoiceSuccessfullyPaid; - _onInvoicePayFailed = onInvoicePayFailed; + _onInvoiceSuccessfullyPaid = (status, paymentReceipt) => + { + onInvoiceSuccessfullyPaid?.Invoke(status, paymentReceipt); + + _onInvoiceSuccessfullyPaid = null; + _onInvoicePayFailed = null; + }; + + _onInvoicePayFailed = (status) => + { + onInvoicePayFailed?.Invoke(status); + + _onInvoiceSuccessfullyPaid = null; + _onInvoicePayFailed = null; + }; OpenInvoice(url, OnInvoiceSuccessfullyPaid, OnInvoicePayFailed); } diff --git a/Unigram Payment/Source/Runtime/Core/UnigramPaymentSDK.cs b/Unigram Payment/Source/Runtime/Core/UnigramPaymentSDK.cs index 727c03e..0fa5d1f 100644 --- a/Unigram Payment/Source/Runtime/Core/UnigramPaymentSDK.cs +++ b/Unigram Payment/Source/Runtime/Core/UnigramPaymentSDK.cs @@ -49,6 +49,8 @@ public static UnigramPaymentSDK Instance [Tooltip("Store all items for purchase, to add new ones call 'Create -> Unigram Payment -> Saleable Item' and add it to the list.")] [SerializeField, Space] private SaleableItemsStorage _itemsStorage; + private SaleableItem _currentPurchaseItem; + private PaymentReceiptData _lastPaymentReceipt; /// @@ -136,7 +138,8 @@ public void Initialize() AuthorizeClient((tokenClaimed) => { OnInitialize(true); - },() => + }, + () => { OnInitialize(false); }); @@ -158,18 +161,20 @@ public void CreateInvoice(SaleableItem item) return; } + _currentPurchaseItem = item; + StartCoroutine(BotAPIBridge.CreateInvoice(item, (invoiceLink) => { LastInvoiceLink = invoiceLink; if (LastInvoiceLink == null) { - OnInvoiceLinkCreateFail(); + OnInvoiceLinkCreateFail(item.Id); return; } - OnInvoiceLinkCreate(LastInvoiceLink); + OnInvoiceLinkCreate(item.Id, LastInvoiceLink); })); } @@ -189,7 +194,7 @@ public void OpenInvoice(string invoiceUrl) { if (receipt == null) { - OnItemPurchaseFail(); + OnItemPurchaseFail(_currentPurchaseItem); return; } @@ -199,7 +204,7 @@ public void OpenInvoice(string invoiceUrl) } else if (status is PaymentStatus.cancelled or PaymentStatus.failed) { - OnItemPurchaseFail(); + OnItemPurchaseFail(_currentPurchaseItem); } }); } @@ -217,7 +222,7 @@ public void Refund(PaymentReceiptData receipt) StartCoroutine(BotAPIBridge.RefundPayment(telegramId, receipt.TransactionId, (isSuccess) => { - OnRefundTransactionFinish(isSuccess); + OnRefundTransactionFinish(LastRefundedTransaction, isSuccess); UnigramPaymentLogger.Log($"Refund process finished with result: {isSuccess}"); })); @@ -327,12 +332,15 @@ private void CreateInstance() private void OnSessionTokenRefresh() => OnSessionTokenRefreshed?.Invoke(); private void OnSessionTokenRefshFail() => OnSessionTokenRefreshFailed?.Invoke(); - private void OnInvoiceLinkCreate(string invoiceUrl) => OnInvoiceLinkCreated?.Invoke(invoiceUrl); - private void OnInvoiceLinkCreateFail() => OnInvoiceLinkCreateFailed?.Invoke(); + private void OnInvoiceLinkCreate(string itemId, + string invoiceUrl) => OnInvoiceLinkCreated?.Invoke(itemId, invoiceUrl); + private void OnInvoiceLinkCreateFail(string itemId) => OnInvoiceLinkCreateFailed?.Invoke(itemId); private void OnItemPurchase(PaymentReceiptData receipt) => OnItemPurchased?.Invoke(receipt); - private void OnItemPurchaseFail() => OnItemPurchaseFailed?.Invoke(); + private void OnItemPurchaseFail(SaleableItem failedPurchaseItem) => + OnItemPurchaseFailed?.Invoke(failedPurchaseItem); - private void OnRefundTransactionFinish(bool isSuccess) => OnRefundTransactionFinished?.Invoke(isSuccess); + private void OnRefundTransactionFinish(string transactionId, + bool isSuccess) => OnRefundTransactionFinished?.Invoke(transactionId, isSuccess); } } \ No newline at end of file