# Payments Gateway Use Case Build Guide *If you haven’t already, [Sign Up for Sandbox Access](https://rail.io/contact-us) to get your client ID and secret to work through this Build Guide!* ## What is the Payment Gateway use case? With the Payment Gateway use case, you or your customers can safely accept payments in stablecoins, cryptocurrencies, and settle to fiat or another digital currency of choice. Create invoices, add payors and track payments in an automated fashion. Payment Gateway High Level Flow ## Build Guide This guide should take no longer than 10 minutes and will allow you to execute a typical payment gateway flow > Note: In sandbox, auto-wire won't work. In production, the wire is automatically sent to a linked fiat account that is setup during onboarding. You’ll open payment/settlement accounts for a corporate customer we have pre-created for you, setup a payor, generate a deposit address for the payor and then transfer in the funds. The platform handles the rest including the allocation, exchange, settlement, wiring, AML, Fraud, wallets and gas fees. By the end of this guide you will have: 1. [Authenticated your request](#1-authenticating-your-request) 2. [Created payment and settlement accounts for a corporate customer](#2-creating-the-payment-and-settlement-accounts) 3. [Linked the payment and settlement accounts](#3-linking-the-accounts) 4. [Created a payor and generated a payment address](#4-creating-your-payor-and-generating-a-payment-address) 5. [Received crypto payment and auto-settled into fiat](#5-receiving-your-payments-and-exchanging-your-currencies) Let’s go! ## 1. Authenticating your request Every request you make to a Rail API endpoint requires an `AUTH_TOKEN`. We secure our endpoints using standards based OAuth2 Client Credentials Grant and scopes. This makes authenticating secure and easy. To obtain the `AUTH_TOKEN` , you will need to authorize your account using your `BASE_64_ENCODED_CLIENTID_AND_SECRET` with the scopes you require: ```bash curl --location --request POST 'https://auth.layer2financial.com/oauth2/ausbdqlx69rH6OjWd696/v1/token?grant_type=client_credentials&scope=customers:read+customers:write+accounts:read+accounts:write+exchanges:read+exchanges:write' \ --header 'Accept: application/json' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Cache-Control: no-cache' \ --header 'Authorization: Basic {BASE_64_ENCODED_CLIENTID_AND_SECRET}' \ ``` This gives you a response object containing your `AUTH_TOKEN:` ```javascript { "token_type":"Bearer", "expires_in":43200, "access_token": AUTH_TOKEN, "scope":"customers:read customers:write accounts:write accounts:read exchanges:read exchanges:write" } ``` The scopes we’ll need for this tutorial are: - `customers:read` and `customers:write` so we can create our customer and get information about them. - `accounts:read` and `accounts:write` so we can create our accounts and get information about them. - `transfers:read` and `transfers:write` so we can create our transfers and get information about them. - `exchanges:read` and `exchanges:write` so we can create our exchanges and get information about them The full list of scopes are available [here](/guides/authentication). With that done, we can get on to setting up accounts for a corporate customer we have pre-created for you, Quorum Coffee, with a customer_id of `QUORUMCOFFEE001`. To learn how to create a customer, check out [Applications](/guides/applications). ## 2. Creating the payment and settlement accounts We need two accounts, a **crypto payment account** and a **fiat deposit account** for fiat settlement: 1. When a customer of Quorum Coffee buys a coffee they can pay in USDC. This USDC goes into Quorum Coffee’s **crypto payment account**. 2. This crypto is then auto-traded to a **fiat deposit account** The settlement process then occurs every evening and the funds are withdrawn from the fiat deposit account to an external account. > Note: The settlement and wiring steps are only active in production. To create these two accounts we need: - The `customer_id` for Quorum Coffee, `QUORUMCOFFEE001`. - An `account_id` for each account that will be the unique identifier for that specific account. Here we’ll use `QUORUMCOFFEE001_USDC.001` as our crypto account id and `QUORUMCOFFEE001_USD.001` for our fiat account. - The `product_id`: This is the type of account to be opened. You can configure your own products, but for our accounts we want to set up a `PAYMENT_FORT_CRYPTO`account for our USDC crypto account and a `DEPOSIT_BASIC`account for our USD fiat account. - The `asset_type_id`: The asset type that the accounts are going to use. Our crypto account is going to be denominated in USDC, so the `asset_type_id` is `ETHEREUM_GOERLI_USDC` (`asset_type_id`s are in the form of `{BLOCKCHAIN}_{NETWORK}_{CURRENCY_CODE}`). Our fiat account is in USD, so the asset_type_id is `FIAT_TESTNET_USD`. ### Creating our Crypto account With all that information, we can set up our crypto account first. We’ll pass the information set out above in a data object and `POST` to our payments accounts API endpoint: ```bash curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/payments' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' \ --data-raw '{ "customer_id": "QUORUMCOFFEE001", "account_to_open": { "account_id": "QUORUMCOFFEE001_USDC.001", "product_id": "PAYMENT_FORT_CRYPTO", "asset_type_id": "ETHEREUM_GOERLI_USDC" } } ``` The response contains the `account_id` that you passed in : ```javascript { data: { id: "QUORUMCOFFEE001_USDC.001" } } ``` You can use that `ACCOUNT_ID` and a `GET` request to the `/accounts/payments/` endpoint to retrieve information about this account: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/payments/QUORUMCOFFEE001_USDC.001' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response object will have the status of the account, product and asset type ids, and the current and available balance: ```jsx { data: { id: "QUORUMCOFFEE001_USDC.001", status: "OPEN", asset_type_id: "ETHEREUM_GOERLI_USDC", product_id: "PAYMENT_FORT_CRYPTO", current_balance: 0, available_balance: 0 } } ``` ### Creating our fiat account For our fiat account, we’re going to call the *deposits* account API endpoint: ```bash curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/deposits' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' \ --data-raw '{ "customer_id": "QUORUMCOFFEE001", "account_to_open": { "account_id": "QUORUMCOFFEE001_USD.001", "product_id": "DEPOSIT_BASIC", "asset_type_id": "FIAT_TESTNET_USD" } }' ``` The response contains the `account_id` that you passed in : ```jsx { data: { id: "QUORUMCOFFEE001_USD.001" } } ``` Again, we can use that `account_id` and a `GET` request to the `accounts/deposits/${account_id}` endpoint to retrieve information about this account: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUMCOFFEE001_USD.001' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response object will have the status of the account, product and asset type ids, and the current and available balance: ```jsx { data: { id: "QUORUMCOFFEE001_USD.001", status: "OPEN", asset_type_id: "FIAT_TESTNET_USD", product_id: "DEPOSIT_BASIC", current_balance: 0, available_balance: 0 } } ``` That’s it for creating our accounts. ## 3. Linking the accounts We now have to link the 2 accounts together. We want them linked so that when a the crypto is deposited into the `QUORUMCOFFEE001_USDC.001` USDC account, it is auto-exchanged into the `QUORUMCOFFEE001_USD.001` USD account, auto-settled each evening and then auto-wired out the following morning. > NOTE: To link your accounts together, you can reach out to the Rail team via our [website](https://rail.io/contact-us) or [support@layer2financial.com](mailto:support@layer2financial.com). The ability to do this yourself is coming in Q1, 2023. That completes the basic setup. Now its time to transact. ## 4. Creating your payor and generating a payment address Next up we have to create payors for the customer. In our scenario, they are the people buying the cup of Quorum coffee. Payors are assigned to customers, so to create a new payor we need the `customer_id` from above, and an `id` for the payor. As with customers and accounts, it makes sense to use [External Entity Identifiers](/guides/externalidentifiers) for the `id` for payors: ```bash curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/customers/QUORUMCOFFEE001/payor' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' \ --data-raw '{ "id": "qc_payor_001", "first_name": "Jack", "last_name": "Smith" }' ``` The response object will be the `payor_id` of the new payor: ```javascript { 'data': { 'payor_id': 'qc_payor_001' } } ``` Once you have a payor, then you can get a payment address. This is the address any payment by that payor is going to go to. We need three pieces of information to get this address: - The `account_id` for our crypto account. In this case, that is `QUORUMCOFFEE001_USDC.001`. - The payment `amount`. We’ll suppose we’re buying a $5 coffee here, but paying in the equivalent 5 USDC - The `payor_id`, which here is ‘qc_payor_001’ You can also optionally add a `payment_reference`, such as an invoice number, again for easier internal reconciliation by the merchant. `POST` this data to the payments endpoint: ```bash curl --location --request POST 'https://sandbox.layer2financial.com/api/v1/accounts/payments/QUORUMCOFFEE001_USDC.001/address' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' \ --data-raw '{ "amount": "5", "payor_id": "qc_payor_001", "payment_reference": "00000001" }' ``` This will return an object with the `wallet_address` that will be passed on to the payors. The `wallet_address` is the main attribute there, but we can also see information about our asset type and currency. We can also get information about the ‘lease’ on that particular address. The lease is important in considering which type of addresses the payors need for any given customer: - If `lease_enforced` is set to `False`, you get a fixed address for each payor account. This payor will always have the same wallet throughout their lifetime as a customer of the merchant. This is useful for recurring payors. - `lease_enforced` is set to `True` rotating addresses will be used. Rotating addresses help optimize blockchain fees and work best when an address is for single time usage. Here `lease_enforced` is set to `False` so we have a fixed address for this customer (though in a coffee shop scenario, a rotating address would also work). ```tsx { 'data': { 'id': '619662b2-51de-46c1-96e7-cd46d3b87ff0', 'wallet_address': '0x91C9F15E5Aa93DE0B520c9E2Ced837c2Cb1bb9c3', 'signed_wallet_address': None, 'asset_type': 'CRYPTO', 'currency': 'USDC_TG', 'asset_type_id': 'ETHEREUM_GOERLI_USDC', 'lease_enforced': False, 'lease_end_date': None, 'lease_transaction_limit': None, 'lease_value_limit': None } } ``` ## 5. Receiving your payments and exchanging your currencies To test this out, all we need is the `wallet_address` from above to send a 5 USDC payment in on behalf of the payor, Jack Smith. With the `wallet_address`, you can deposit funds into this account using any Goerli faucet, such as the [Alchemy Goerli faucet](https://goerlifaucet.com/) (requires signup) or [All That Node](https://www.allthatnode.com/faucet/ethereum.dsrv) (no signup needed). You can also send a note at [support@layer2financial.com](mailto:support@layer2financial.com). Once you’ve deposited funds, you can check they are in the account by querying the `accounts/payments/{ACCOUNT_ID}` endpoint again: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/payments/QUORUMCOFFEE001_USDC.001' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The `current_balance` should have changed to the amount you added to the account: ```javascript { data: { id: "QUORUMCOFFEE001_USDC.001", status: "OPEN", asset_type_id: "ETHEREUM_GOERLI_USDC", product_id: "PAYMENT_FORT_CRYPTO", current_balance: 5.000000000000000000, available_balance: 0 } } ``` If the `available_balance` is showing `0` as above, the transaction is still going through all the AML and Fraud checks. You can check on the status of the transactions by querying the `accounts/payments/{ACCOUNT_ID}/transactions` endpoint. ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/payments/QUORUMCOFFEE001_USDC.001/transactions' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response object will tell us what we need to know about our transactions: ```javascript { 'data': { 'transactions': [ { 'payor_id:'qc_payor_001', 'payor_amount':5, 'payor_reference_id':'00000001','external_transaction_id':'0xd0eba9c73dd9z31f0289c4631672c2438f54a40df359544d1775440f4dcae6d27','id':'3b8e38ad-ba09-46e8-9a40-fed2acb12956', 'value':5.000000000000000000 'transaction_date': '2022-11-09T09:46:55.519763', 'transaction_posted_date': '2022-11-09T09:46:55.519763', 'transaction_status': 'PENDING', 'description': 'TRANSFER_IN', 'transaction_type': 'TRANSFER_IN' } ] } } ``` As we can see, our `transaction_status` is still `PENDING`. Once the transaction is ACCEPTED, we’ll see the funds in our `available_balance`. You may find that your transaction was already `POSTED` by the time you have checked. That’s ok - we’re just moving faster than we planned! Once the `available_balance` is updated, the USDC will be auto-traded into USD into the fiat account, `QUORUMCOFFEE001_USD.001`. After a few minutes you’ll be able to `GET` that information with the `account_id` using the `accounts/deposits/{$account_id}` endpoint ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUMCOFFEE001_USD.001' \ --header 'Authorization: Bearer {$AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response object shows the payment has been applied: ```javascript { 'data': { 'id': 'QUORUMCOFFEE001_USD.001', 'status': 'OPEN', 'asset_type_id': 'FIAT_TESTNET_USD', 'product_id': 'DEPOSIT_BASIC', 'current_balance': 5, 'available_balance': 5 } } ``` You can check on the status of the transactions by querying the `accounts/payments/${account_id}/transactions` endpoint. If we call it for our USDC account, we’ll see both the transfer in from the payor, and the trade out to our fiat account: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/payments/QUORUMCOFFEE001_USDC.001/transactions' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response gives us both the `TRANSFER_IN` and the `TRADE` out: ```javascript { data: { 'transactions': [ { 'payor_id': 'qc_payor_001', 'payor_amount': 5, 'external_transaction_id': '0xd0eba9c73dd9z31f0289c4631672c2438f54a40df359544d1775440f4dcae6d27', 'id': '5c104b2c-b845-496b-9813-539a26f92920', 'value': 5.0, 'transaction_date': '2022-11-19T14:01:42.365466', 'transaction_posted_date': '2022-11-19T14:05:40.923995', 'transaction_status': 'POSTED', 'description': 'TRANSFER_IN', 'transaction_type': 'TRANSFER_IN' }, { 'id': 'e23a56a9-edc0-499a-aa14-32e6f0b3ae0b', 'value': -5.0, 'transaction_date': '2022-11-19T14:47:03.437104', 'transaction_posted_date': '2022-11-19T14:47:26.295521', 'transaction_status': 'POSTED', 'description': 'TRADE', 'transaction_type': 'TRADE' } ] } } ``` When we call it for our USD fiat account using the `accounts/deposits/${account_id}/transactions` endpoint, we see just the TRADE in: ```bash curl --location --request GET 'https://sandbox.layer2financial.com/api/v1/accounts/deposits/QUORUMCOFFEE001_USD.001/transactions' \ --header 'Authorization: Bearer {AUTH_TOKEN}' \ --header 'Content-Type: application/json' ``` The response: ```javascript { 'data': { 'transactions': [ { 'id': '1e28d03b-1bfe-4a91-82e5-b76bea658e94', 'value': 5, 'transaction_date': '2022-11-19T14:47:26.295521', 'transaction_posted_date': '2022-11-19T14:47:26.295521', 'transaction_status': 'PENDING', 'description': 'TRADE', 'transaction_type': 'TRADE' } ] } } ``` Once the transaction is auto-settled, the `transaction_status` will change to `POSTED`. In production, settlement happens nightly during the weekday and the funds are automatically wired out the next business day. That’s it. The merchant is set up to receive payments from their customers. ## Summary Let’s review: 1. Authenticate using the OAuth Endpoint to get the `AUTH_TOKEN`. 2. Create the 2 accounts, the USDC one to receive the payment and a USD one to settle into. To setup the USDC account, use the [`accounts/payments` endpoint](/api-docs/openapi/rail-spec#operation/openPaymentAccount). To setup the USD settlement account, use the [`accounts/deposits` endpoint](/api-docs/openapi/rail-spec#operation/openAccount). 3. Link the 2 accounts by reaching out to the Rail team. 4. Setup a payor using the [`customers/{CUSTOMER_ID}/payor` endpoint](/api-docs/openapi/rail-spec#operation/onboardPayor) and generate a payment `wallet_address` for the USDC account using the [`accounts/payments/{ACCOUNT_ID}/address`](/api-docs/openapi/rail-spec#operation/getPaymentAddress). 5. Deposit funds into the USDC account `wallet_address` using external faucets and query the balance of the the account using the [`accounts/payments/{ACCOUNT_ID}` endpoint](/api-docs/openapi/rail-spec#operation/getPaymentAccount). Funds are auto-exchanged into the USD account. Check transactions in the USD account using the [`accounts/deposits/{ACCOUNT_ID}/transactions` endpoint](/api-docs/openapi/rail-spec#operation/getTransactions). To dive deeper into what you can do on the Rail platform, head to our [API documentation](/api-docs/openapi/rail-spec). **Contact Us** - We’d love to hear your thoughts, and you can contact the team via slack, [website](https://rail.io/contact-us) or email [support@layer2financial.com](mailto:support@layer2financial.com).