Split an incoming payment
Esta página aún no está disponible en tu idioma.
Imagine making a purchase from an online marketplace. From your perspective, you’re sending a single payment to a merchant in exchange for a good. Behind the scenes, the marketplace receives a portion of the payment as a service fee.
There’s a few ways for the marketplace to collect their fee. For example, it could receive the full amount, deduct the fee, then send the rest to the merchant. However, holding funds for the merchant, even for a second, requires compliance with certain financial rules and regulations. A better way is to ensure both parties only receive the amount they’re supposed to receive, directly from the user.
Remember, Open Payments doesn’t execute payments or touch money in any way. It’s used to issue payment instructions before any money movement occurs. Examples of payment instructions are, “of the $6 purchase, pay the marketplace $1 and the merchant $5.” This way, funds meant for one party never pass through the other party.
For this guide, you’ll assume the role of a platform operator of an online marketplace. The guide explains how to split a customer’s $100 USD payment into two incoming payments. The merchant will receive 99% of the payment while you keep 1% as a fee.
The three parties involved in the transaction are the:
- Customer: the purchaser of a good or service on the marketplace
- Merchant: the seller of a good or service on the marketplace
- Platform operator: you, as the operator of the marketplace
Endpoints
Section titled “Endpoints”-
Get a Wallet address
-
Grant Request
-
Create Incoming Payment
-
Create a Quote
-
Create an Outgoing Payment
1. Get wallet address information
Section titled “1. Get wallet address information”When a customer initiates a payment, your platform must get wallet address information for the customer, the merchant, and you, as the operator.
Let’s assume your wallet address already saved to your platform, as is the merchant’s. Let’s also assume the customer provided their wallet address at the beginning of the checkout flow.
Call the Get Wallet Address API for each address.
const customerWalletAddress = await client.walletAddress.get({ url: 'https://cloudninebank.example.com/customer'})
const merchantWalletAddress = await client.walletAddress.get({url: 'https://happylifebank.example.com/merchant'})
const platformWalletAddress = await client.walletAddress.get({url: 'https://coolwallet.example.com/platform'})
2. Request incoming payment grants
Section titled “2. Request incoming payment grants”Use the merchant and platform authServer
details, received in the previous step, to call the Grant Request API.
The purpose of these calls is to obtain access tokens that allow your platform to request an incoming payment resource be created on the merchant’s wallet account and your wallet account.
const merchantIncomingPaymentGrant = await client.grant.request( { url: merchantWalletAddress.authServer }, { access_token: { access: [ { type: "incoming-payment", actions: ["read", "create"], }, ], }, },);
const platformIncomingPaymentGrant = await client.grant.request( { url: platformWalletAddress.authServer }, { access_token: { access: [ { type: "incoming-payment", actions: ["read", "create"], }, ], }, },);
3. Request the creation of incoming payment resources
Section titled “3. Request the creation of incoming payment resources”Use the access tokens returned in the previous responses to call the Create Incoming Payment API.
The purpose of these calls is to request an incoming payment resource be created on the merchant’s wallet account and your wallet account.
Remember that you’re retaining 1% of the customer’s $100 USD payment as a fee.
Include the following in both requests, along with any other required parameters.
incomingAmount
objectvalue
- The maximum amount to pay into a given wallet address.assetCode
- The code used by the wallet account, provided in the Get Wallet Address API’s response.assetScale
- The scale used by the wallet account, provided in the Get Wallet Address API’s response.
The full value
of $100 is 10000
. The merchant will receive 99% (9900
) and you will receive 1% (100
).
const merchantIncomingPayment = await client.incomingPayment.create( { url: merchantWalletAddress.resourceServer, accessToken: merchantIncomingPaymentGrant.access_token.value }, { walletAddress: merchantWalletAddress.id, incomingAmount: { value: '9900', assetCode: 'USD', assetScale: 2 }, },)
const platformIncomingPayment = await client.incomingPayment.create( { url: platformWalletAddress.resourceServer, accessToken: platformIncomingPaymentGrant.access_token.value }, { walletAddress: platformWalletAddress.id, incomingAmount: { value: '100', assetCode: 'USD', assetScale: 2 }, },)
4. Request a quote grant
Section titled “4. Request a quote grant”Use the customer’s authServer
details, received in Step 1, to call the Grant Request API.
The purpose of this call is to obtain an access token that allows your platform to request quote resources be created on the customer’s wallet account.
const customerQuoteGrant = await client.grant.request( { url: customerWalletAddress.authServer }, { access_token: { access: [ { type: 'quote', actions: ['create', 'read'] } ] } })
5. Request the creation of quote resources
Section titled “5. Request the creation of quote resources”Use the access token, received in the previous step, to call the Create Quote API.
The purpose of this call is to request a quote resource be created on the customer’s wallet account. Since the customer needs a quote resource for the merchant and the platform, we’ll call the API twice using the same access token.
First, let’s request a quote resource associated with the merchant. The request must contain the receiver
, which is the merchant’s incoming payment id
, along with any other required parameters. The id
was returned in the Create an Incoming Payment API response in Step 3.
Next, call the Create Quote API again and request a quote resource associated with the platform’s incoming payment id
.
const merchantQuote = await client.quote.create( { url: customerWalletAddress.resourceServer, accessToken: customerQuoteGrant.access_token.value }, { method: 'ilp', walletAddress: customerWalletAddress.id, receiver: merchantIncomingPayment.id })
const platformQuote = await client.quote.create( { url: customerWalletAddress.resourceServer, accessToken: customerQuoteGrant.access_token.value }, { method: 'ilp', walletAddress: customerWalletAddress.id, receiver: platformIncomingPayment.id })
Each response returns a receiveAmount
, a debitAmount
, and other required information.
receiveAmount
- TheincomingAmount
value from the incoming payment resourcedebitAmount
- The amount the customer must pay toward the incoming payment resource (receiveAmount
plus any applicable fees)
6. Request an interactive outgoing payment grant
Section titled “6. Request an interactive outgoing payment grant”Use the customer’s authServer
information received in Step 1 to call the Grant Request API.
The purpose of this call is to obtain an access token that allows your platform to request outgoing payment resources be created on the customer’s wallet account.
Include the following in the request, along with any other required parameters.
limits
objectreceiveAmount
objectvalue
- The total amount the customer has agreed to pay.assetCode
- The code used by the wallet account, provided in the Get Wallet Address API’s response.assetScale
- The scale used by the wallet account, provided in the Get Wallet Address API’s response.
const pendingCustomerOutgoingPaymentGrant = await client.grant.request( { url: customerWalletAddress.authServer }, { access_token: { access: [ { identifier: customerWalletAddress.id, type: 'outgoing-payment', actions: ['read', 'create'], limits: { receiveAmount: { assetCode: 'USD', assetScale: 2, value: '10000' } } } ] }, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'http://localhost:3344', nonce: NONCE } } })
7. Start interaction with the customer
Section titled “7. Start interaction with the customer”Once your client receives the authorization server’s response, it must send the user to the interact.redirect
URI contained in the response. This starts the interaction flow.
The response also includes a continue
object, which is essential for managing the interaction and obtaining explicit user consent for outgoing payment grants. The continue
object contains an access token and a URI that the client will use to finalize the grant request after the user has completed their interaction with the identity provider (IdP). This ensures that the client can securely obtain the necessary permissions to proceed with the payment process.
{ "interact": { "redirect": "https://auth.interledger-test.dev/4CF492MLVMSW9MKMXKHQ", "finish": "4105340a-05eb-4290-8739-f9e2b463bfa7" }, "continue": { "access_token": { "value": "33OMUKMKSKU80UPRY5NM" }, "uri": "https://auth.interledger-test.dev/continue/4CF492MLVMSW9MKMXKHQ", "wait": 30 }}
8. Finish interaction with the customer
Section titled “8. Finish interaction with the customer”The user interacts with the authorization server through the server’s interface and approves or denies the grant.
Provided the user approves the grant, the authorization server:
- Sends the user to your client’s previously defined
finish.uri
. The means by which the server sends the user to the URI is out of scope, but common options include redirecting the user from a web page and launching the system browser with the target URI. - Secures the redirect by adding a unique hash, allowing your client to validate the
finish
call, and an interaction reference as query parameters to the URI.
9. Request a grant continuation
Section titled “9. Request a grant continuation”In our example, we’re assuming the IdP the customer interacted with has a user interface. When the interaction completes, the customer returns to your platform. Now your platform can make a continuation request for the outgoing payment grant.
Call the Grant Continuation Request API. The purpose of this call is to request an access token that allows your platform to request outgoing payment resources be created on the customer’s wallet account.
Issue the request to the continue uri
provided in the initial outgoing payment grant response (Step 6). For example:
POST https://auth.interledger-test.dev/continue/4CF492MLVMSW9MKMXKHQ
Include the interact_ref
returned in the redirect URI’s query parameters.
const customerOutgoingPaymentGrant = await client.grant.continue( { url: pendingCustomerOutgoingPaymentGrant.continue.uri, accessToken: pendingCustomerOutgoingPaymentGrant.continue.access_token.value }, { interact_ref: interactRef })
10. Request the creation of outgoing payment resources
Section titled “10. Request the creation of outgoing payment resources”Recall that the Create Quote API responses for the merchant and your platform (Step 5) both included a debitAmount
and a receiveAmount
. The responses also included an id
which is a URL to identify each quote.
Because the quotes contain debit and receive amounts, we won’t specify any other amounts when making a Create Outgoing Payment API request. Instead, we will specify a quoteId
.
Use the access tokens returned in Step 5 to call the Create Outgoing Payment API.
The purpose of these calls is to request an outgoing payment resource be created on the customer’s wallet account: one for the merchant and one for your platform. Include the appropriate quoteId
in each request.
const customerOutgoingPayment = await client.outgoingPayment.create( { url: customerWalletAddress.resourceServer, accessToken: customerOutgoingPaymentGrant.access_token.value }, { walletAddress: customerWalletAddress.id, quoteId: merchantQuote.id })
const customerOutgoingPayment = await client.outgoingPayment.create( { url: customerWalletAddress.resourceServer, accessToken: customerOutgoingPaymentGrant.access_token.value }, { walletAddress: customerWalletAddress.id, quoteId: platformQuote.id })