Create a split-payments application
This tutorial guides you to build an application that supports split-payments using the Open Payments SDK. Split-payments are common on online marketplaces where multiple merchants sell their goods and services on one platform. When a customer purchases an item on the marketplace, you, as the platform operator, can split the payment between yourself and the merchant according to a percentage split. Airbnb and Uber are examples of platforms where payments made by guests and riders are split between the platform on one side and hosts and drivers on the other, respectively.
In the split-payments use case, there are three parties to the transaction:
- Customer: the purchaser of a good or service on the marketplace
- Merchant: the seller of a good or service on the marketplace
- Platform: the operator of the marketplace
In this tutorial you will assume the role of the platform operator integrating split-payments using Open Payments with your online marketplace.
Endpoints
-
Grant Request
-
Get a Wallet address
-
Create a Quote
-
Create an Incoming Payment
-
Create an Outgoing Payment
Prerequisites
- Node 20
- A package manager such as NPM or PNPM
-
Open Payments SDK
- TSX
Additional configuration
Add "type": "module"
to package.json
Add the following to tsconfig.json
{ "compilerOptions": { "target": "ES2022", "module": "ES2022" }}
Import dependencies
Import createAuthenticatedClient
from the Open Payments SDK package.
Import dependencies
import { createAuthenticatedClient } from "@interledger/open-payments";
Copied! Create an authenticated Open Payments client
Create an Open Payments-authenticated client by providing the following properties:
walletAddressURL
: your Open Payments-enabled wallet address that your client will use to authenticate itself to one or more authorization servers.privateKey
: the EdDSA-Ed25519 key or preferably the absolute or relative file path to the key that is bound to your wallet address. A public key signed with this private key must be made available as a public JWK document at{walletAddressUrl}/jwks.json
url.keyId
: the identifier of the private key and the corresponding public key.
Initialize Open Payments client
const client = await createAuthenticatedClient({
walletAddressUrl: WALLET_ADDRESS,
privateKey: PRIVATE_KEY_PATH,
keyId: KEY_ID,
});
Copied! Steps
1. Fetch wallet addresses
Fetch the wallet addresses of the buyer, merchant, and your marketplace platform. In this example, we assume each party has their Open Payments-enabled account with a different account servicing entity.
const customerWalletAddress = await client.walletAddress.get({ url: 'https://gatehub.net/buyer'})
const merchantWalletAddress = await client.walletAddress.get({ url: 'https://chimoney.io/merchant'})
const platformWalletAddress = await client.walletAddress.get({ url: 'https://interledger.app/platform'})
2. Request Incoming Payment grants
Request incomingPayment
grants from your wallet authorization server as well as from the merchant wallet authorization server.
const merchantGrant = await client.grant.request( { url: merchantWalletAddress.authServer, }, { access_token: { access: [ { type: "incoming-payment", actions: ["read", "create"], }, ], }, },);
const platformGrant = await client.grant.request( { url: platformWalletAddress.authServer, }, { access_token: { access: [ { type: "incoming-payment", actions: ["read", "create"], }, ], }, },);
3. Create Incoming Payment resources
Create incomingPayment
resources using the access tokens returned from the authorizations servers in step 2. Since this example assumes the merchant and the platform operator are using different account servicing entities, the incomingPayment
resources will be created on the respective resource servers.
In this example you will split the payment and give the merchant 99% on a $100 payment, while retaining 1% for your fee as the platform operator.
const merchantIncomingPayment = await client.incomingPayment.create( { url: new URL(WALLET_ADDRESS).origin, accessToken: INCOMING_PAYMENT_ACCESS_TOKEN }, { merchantWalletAddress: WALLET_ADDRESS, incomingAmount: { value: '9900', assetCode: 'USD', assetScale: 2 }, expiresAt: new Date(Date.now() + 60_000 * 10).toISOString() })
const platformIncomingPayment = await client.incomingPayment.create( { url: new URL(WALLET_ADDRESS).origin, accessToken: INCOMING_PAYMENT_ACCESS_TOKEN }, { platformWalletAddress: WALLET_ADDRESS, incomingAmount: { value: '100', assetCode: 'USD', assetScale: 2 }, expiresAt: new Date(Date.now() + 60_000 * 10).toISOString() })
4. Request quote grants
Request two quote grants from the customer wallet authorization server, as there are two recipients on this payment.
const grant = await client.grant.request( { url: walletAddress.authServer }, { access_token: { access: [ { type: 'quote', actions: ['create', 'read'] } ] } })
5. Request quotes
Request two quote resources from the customer wallet resource server using the access tokens returned in Step 4. Specify the respective incomingPayment
URLs returned in Step 3 for each of the quotes.
const quote = await client.quote.create( { url: new URL(WALLET_ADDRESS).origin, accessToken: QUOTE_ACCESS_TOKEN }, { method: 'ilp', walletAddress: WALLET_ADDRESS, receiver: INCOMING_PAYMENT_URL })
5. Request an interactive outgoing payment grant
Request an outgoingPayment
grant from the customer wallet authorization server.
Add the limits
object with the following properties:
receiveAmount
: the total maximum amount to be received and then split between the merchant’s wallet and your platform’s wallet.
const grant = await client.grant.request( { url: customerWalletAddress.authServer }, { access_token: { access: [ { identifier: merchantWalletAddress.id, type: 'outgoing-payment', actions: ['read', 'create'], limits: { receiveAmount: { assetCode: receiveAmount.assetCode, assetScale: receiveAmount.assetScale, value: receiveAmount.value } } } ] }, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'http://localhost:3344', nonce: NONCE } } })
6. Create outgoing payments
Create two outgoingPayment
resources on the customer wallet resource server using the access token returned by the authorization server in the grant request in step 5.
Add the following properties:
walletAddress
: the URL of the customer wallet address.quoteId
: the URL of the quote specifying the payment amount to be paid to the merchant.
const outgoingPayment = await client.outgoingPayment.create( { url: new URL(WALLET_ADDRESS).origin, accessToken: OUTGOING_PAYMENT_ACCESS_TOKEN }, { walletAddress: WALLET_ADDRESS, quoteId: QUOTE_URL })
Add the following properties:
walletAddress
: the URL of the customer wallet address.quoteId
: the URL of the quote specifying the payment amount to be paid to the platform.
const outgoingPayment = await client.outgoingPayment.create( { url: new URL(WALLET_ADDRESS).origin, accessToken: OUTGOING_PAYMENT_ACCESS_TOKEN }, { walletAddress: WALLET_ADDRESS, quoteId: QUOTE_URL })