├── .gitignore ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Macs 2 | .DS_Store 3 | 4 | *.key 5 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > This repository is archived. You can find the updated peer-to-peer payment example for the Open Payments Node client at the [open-payments-node](https://github.com/interledger/open-payments-node/tree/main/examples/peer-to-peer) repository. 3 | 4 | # Open Payments Example 5 | 6 | This script sends money between two wallet addresses, using the [Open Payments client](https://github.com/interledger/open-payments/tree/main/packages/open-payments). 7 | 8 | The script creates an incoming payment on the receiving wallet address, and a quote on the sending wallet address (after getting grants for both). It also creates an interactive outgoing payment grant, which will require user interaction. 9 | 10 | The script then finalizes the grant (after it's accepted interactively via the browser), and creates the outgoing payment. 11 | 12 | ### Steps 13 | 14 | 1. Make sure you have NodeJS installed 15 | 2. Run `npm install` 16 | 3. Get a private key, client wallet address and keyId from the [test wallet](https://wallet.interledger-test.dev/), and add them in the script. 17 | 4. Pick a receiving wallet address, and a sending wallet address. 18 | 5. Run `node index.js` 19 | 6. Click on the outputted URL, to accept the outgoing payment grant. 20 | 7. Return to the terminal, hit enter. After this, the script will create the outgoing payment and funds will move between the receiver and the sender! 21 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This script sets up an incoming payment on a receiving wallet address, 3 | * and a quote on the sending wallet address (after getting grants for both of the resources). 4 | * 5 | * The final step is asking for an outgoing payment grant for the sending wallet address. 6 | * Since this needs user interaction, you will need to navigate to the URL, and accept the interactive grant. 7 | * 8 | * To start, please add the variables for configuring the client & the wallet addresses for the payment. 9 | */ 10 | 11 | import { 12 | createAuthenticatedClient, 13 | OpenPaymentsClientError, 14 | isFinalizedGrant, 15 | } from "@interledger/open-payments"; 16 | import readline from "readline/promises"; 17 | 18 | (async () => { 19 | const client = await createAuthenticatedClient({ 20 | walletAddressUrl: "", // Make sure the wallet address starts with https:// (not $), and has no trailing slashes 21 | privateKey: "private.key", 22 | keyId: "", 23 | }); 24 | 25 | const sendingWalletAddress = await client.walletAddress.get({ 26 | url: "", // Make sure the wallet address starts with https:// (not $) 27 | }); 28 | const receivingWalletAddress = await client.walletAddress.get({ 29 | url: "", // Make sure the wallet address starts with https:// (not $) 30 | }); 31 | 32 | console.log( 33 | "Got wallet addresses. We will set up a payment between the sending and the receiving wallet address", 34 | { receivingWalletAddress, sendingWalletAddress } 35 | ); 36 | 37 | // Step 1: Get a grant for the incoming payment, so we can create the incoming payment on the receiving wallet address 38 | const incomingPaymentGrant = await client.grant.request( 39 | { 40 | url: receivingWalletAddress.authServer, 41 | }, 42 | { 43 | access_token: { 44 | access: [ 45 | { 46 | type: "incoming-payment", 47 | actions: ["read", "complete", "create"], 48 | }, 49 | ], 50 | }, 51 | } 52 | ); 53 | 54 | console.log( 55 | "\nStep 1: got incoming payment grant for receiving wallet address", 56 | incomingPaymentGrant 57 | ); 58 | 59 | // Step 2: Create the incoming payment. This will be where funds will be received. 60 | const incomingPayment = await client.incomingPayment.create( 61 | { 62 | url: receivingWalletAddress.resourceServer, 63 | accessToken: incomingPaymentGrant.access_token.value, 64 | }, 65 | { 66 | walletAddress: receivingWalletAddress.id, 67 | incomingAmount: { 68 | assetCode: receivingWalletAddress.assetCode, 69 | assetScale: receivingWalletAddress.assetScale, 70 | value: "1000", 71 | }, 72 | } 73 | ); 74 | 75 | console.log( 76 | "\nStep 2: created incoming payment on receiving wallet address", 77 | incomingPayment 78 | ); 79 | 80 | // Step 3: Get a quote grant, so we can create a quote on the sending wallet address 81 | const quoteGrant = await client.grant.request( 82 | { 83 | url: sendingWalletAddress.authServer, 84 | }, 85 | { 86 | access_token: { 87 | access: [ 88 | { 89 | type: "quote", 90 | actions: ["create", "read"], 91 | }, 92 | ], 93 | }, 94 | } 95 | ); 96 | 97 | console.log( 98 | "\nStep 3: got quote grant on sending wallet address", 99 | quoteGrant 100 | ); 101 | 102 | // Step 4: Create a quote, this gives an indication of how much it will cost to pay into the incoming payment 103 | const quote = await client.quote.create( 104 | { 105 | url: sendingWalletAddress.resourceServer, 106 | accessToken: quoteGrant.access_token.value, 107 | }, 108 | { 109 | walletAddress: sendingWalletAddress.id, 110 | receiver: incomingPayment.id, 111 | method: "ilp", 112 | } 113 | ); 114 | 115 | console.log("\nStep 4: got quote on sending wallet address", quote); 116 | 117 | // Step 5: Start the grant process for the outgoing payments. 118 | // This is an interactive grant: the user (in this case, you) will need to accept the grant by navigating to the outputted link. 119 | const outgoingPaymentGrant = await client.grant.request( 120 | { 121 | url: sendingWalletAddress.authServer, 122 | }, 123 | { 124 | access_token: { 125 | access: [ 126 | { 127 | type: "outgoing-payment", 128 | actions: ["read", "create"], 129 | limits: { 130 | debitAmount: { 131 | assetCode: quote.debitAmount.assetCode, 132 | assetScale: quote.debitAmount.assetScale, 133 | value: quote.debitAmount.value, 134 | }, 135 | }, 136 | identifier: sendingWalletAddress.id, 137 | }, 138 | ], 139 | }, 140 | interact: { 141 | start: ["redirect"], 142 | // finish: { 143 | // method: "redirect", 144 | // // This is where you can (optionally) redirect a user to after going through interaction. 145 | // // Keep in mind, you will need to parse the interact_ref in the resulting interaction URL, 146 | // // and pass it into the grant continuation request. 147 | // uri: "https://example.com", 148 | // nonce: crypto.randomUUID(), 149 | // }, 150 | }, 151 | } 152 | ); 153 | 154 | console.log( 155 | "\nStep 5: got pending outgoing payment grant", 156 | outgoingPaymentGrant 157 | ); 158 | console.log( 159 | "Please navigate to the following URL, to accept the interaction from the sending wallet:" 160 | ); 161 | console.log(outgoingPaymentGrant.interact.redirect); 162 | 163 | await readline 164 | .createInterface({ input: process.stdin, output: process.stdout }) 165 | .question("\nPlease accept grant and press enter..."); 166 | 167 | let finalizedOutgoingPaymentGrant; 168 | 169 | const grantContinuationErrorMessage = 170 | "\nThere was an error continuing the grant. You probably have not accepted the grant at the url (or it has already been used up, in which case, rerun the script)."; 171 | 172 | try { 173 | finalizedOutgoingPaymentGrant = await client.grant.continue({ 174 | url: outgoingPaymentGrant.continue.uri, 175 | accessToken: outgoingPaymentGrant.continue.access_token.value, 176 | }); 177 | } catch (err) { 178 | if (err instanceof OpenPaymentsClientError) { 179 | console.log(grantContinuationErrorMessage); 180 | process.exit(); 181 | } 182 | 183 | throw err; 184 | } 185 | 186 | if (!isFinalizedGrant(finalizedOutgoingPaymentGrant)) { 187 | console.log( 188 | "There was an error continuing the grant. You probably have not accepted the grant at the url." 189 | ); 190 | process.exit(); 191 | } 192 | 193 | console.log( 194 | "\nStep 6: got finalized outgoing payment grant", 195 | finalizedOutgoingPaymentGrant 196 | ); 197 | 198 | // Step 7: Finally, create the outgoing payment on the sending wallet address. 199 | // This will make a payment from the outgoing payment to the incoming one (over ILP) 200 | const outgoingPayment = await client.outgoingPayment.create( 201 | { 202 | url: sendingWalletAddress.resourceServer, 203 | accessToken: finalizedOutgoingPaymentGrant.access_token.value, 204 | }, 205 | { 206 | walletAddress: sendingWalletAddress.id, 207 | quoteId: quote.id, 208 | } 209 | ); 210 | 211 | console.log( 212 | "\nStep 7: Created outgoing payment. Funds will now move from the outgoing payment to the incoming payment.", 213 | outgoingPayment 214 | ); 215 | 216 | process.exit(); 217 | })(); 218 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-payments-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@interledger/open-payments": "^6.13.2" 15 | } 16 | } 17 | --------------------------------------------------------------------------------