Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 1 addition & 83 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,86 +41,4 @@ dist
**/.terraform

tmp
.spectral.json
styles/.vale-config/3-MDX.ini
styles/Google/Acronyms.yml
styles/Google/AMPM.yml
styles/Google/Colons.yml
styles/Google/Contractions.yml
styles/Google/DateFormat.yml
styles/Google/Ellipses.yml
styles/Google/EmDash.yml
styles/Google/Exclamation.yml
styles/Google/FirstPerson.yml
styles/Google/Gender.yml
styles/Google/GenderBias.yml
styles/Google/HeadingPunctuation.yml
styles/Google/Headings.yml
styles/Google/Latin.yml
styles/Google/LyHyphens.yml
styles/Google/meta.json
styles/Google/OptionalPlurals.yml
styles/Google/Ordinal.yml
styles/Google/OxfordComma.yml
styles/Google/Parens.yml
styles/Google/Passive.yml
styles/Google/Periods.yml
styles/Google/Quotes.yml
styles/Google/Ranges.yml
styles/Google/Semicolons.yml
styles/Google/Slang.yml
styles/Google/Spacing.yml
styles/Google/Spelling.yml
styles/Google/Units.yml
styles/Google/vocab.txt
styles/Google/We.yml
styles/Google/Will.yml
styles/Google/WordList.yml
styles/proselint/Airlinese.yml
styles/proselint/AnimalLabels.yml
styles/proselint/Annotations.yml
styles/proselint/Apologizing.yml
styles/proselint/Archaisms.yml
styles/proselint/But.yml
styles/proselint/Cliches.yml
styles/proselint/CorporateSpeak.yml
styles/proselint/Currency.yml
styles/proselint/Cursing.yml
styles/proselint/DateCase.yml
styles/proselint/DateMidnight.yml
styles/proselint/DateRedundancy.yml
styles/proselint/DateSpacing.yml
styles/proselint/DenizenLabels.yml
styles/proselint/Diacritical.yml
styles/proselint/GenderBias.yml
styles/proselint/GroupTerms.yml
styles/proselint/Hedging.yml
styles/proselint/Hyperbole.yml
styles/proselint/Jargon.yml
styles/proselint/LGBTOffensive.yml
styles/proselint/LGBTTerms.yml
styles/proselint/Malapropisms.yml
styles/proselint/meta.json
styles/proselint/Needless.yml
styles/proselint/Nonwords.yml
styles/proselint/Oxymorons.yml
styles/proselint/P-Value.yml
styles/proselint/RASSyndrome.yml
styles/proselint/README.md
styles/proselint/Skunked.yml
styles/proselint/Spelling.yml
styles/proselint/Typography.yml
styles/proselint/Uncomparables.yml
styles/proselint/Very.yml
styles/write-good/Cliches.yml
styles/write-good/E-Prime.yml
styles/write-good/Illusions.yml
styles/write-good/meta.json
styles/write-good/Passive.yml
styles/write-good/README.md
styles/write-good/So.yml
styles/write-good/ThereIs.yml
styles/write-good/TooWordy.yml
styles/write-good/Weasel.yml
.vale.ini
vale
*.key
20 changes: 20 additions & 0 deletions examples/peer-to-peer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Peer-to-Peer Payment Example

This script sends money between two wallet addresses using the [Node Open Payments client](https://github.com/interledger/open-payments-node/tree/main/packages/open-payments).

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.

The script then finalizes the grant (after it's accepted interactively via the browser), and creates the outgoing payment.

### Steps

1. Make sure you have NodeJS installed
2. Run `pnpm install`
3. Get a private key, client wallet address and keyId from an Open Payments enabled wallet, and add them in the script.

> You can use our [test wallet](https://wallet.interledger-test.dev) to create accounts, and generate developer keys for making payments via the Open Payments APIs. Instructions about how to use the test wallet are found at the [Open Payments API documentation](https://openpayments.dev/sdk/before-you-begin/).

4. Pick a receiving wallet address, a sending wallet address, and add them as variables in the script.
5. Run `node index.js`
6. Click on the outputted URL, to accept the outgoing payment grant.
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!
231 changes: 231 additions & 0 deletions examples/peer-to-peer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
/**
* This script sets up an incoming payment on a receiving wallet address,
* and a quote on the sending wallet address (after getting grants for both of the resources).
*
* The final step is asking for an outgoing payment grant for the sending wallet address.
* Since this needs user interaction, you will need to navigate to the URL, and accept the interactive grant.
*
* To start, please add the variables for configuring the client & the wallet addresses for the payment.
*/

import {
createAuthenticatedClient,
OpenPaymentsClientError,
isFinalizedGrant
} from '@interledger/open-payments'
import readline from 'readline/promises'
;(async () => {
// Client configuration
const PRIVATE_KEY_PATH = 'private.key'
const KEY_ID = ''

// Make sure the wallet addresses starts with https:// (not $)
const CLIENT_WALLET_ADDRESS_URL = ''
const SENDING_WALLET_ADDRESS_URL = ''
const RECEIVING_WALLET_ADDRESS_URL = ''

const client = await createAuthenticatedClient({
walletAddressUrl: CLIENT_WALLET_ADDRESS_URL,
keyId: KEY_ID,
privateKey: PRIVATE_KEY_PATH
})

// Step 1: Get the sending and receiving wallet addresses
const sendingWalletAddress = await client.walletAddress.get({
url: SENDING_WALLET_ADDRESS_URL
})
const receivingWalletAddress = await client.walletAddress.get({
url: RECEIVING_WALLET_ADDRESS_URL
})

console.log('\nStep 1: got wallet addresses', {
receivingWalletAddress,
sendingWalletAddress
})

// Step 2: Get a grant for the incoming payment, so we can create the incoming payment on the receiving wallet address
const incomingPaymentGrant = await client.grant.request(
{
url: receivingWalletAddress.authServer
},
{
access_token: {
access: [
{
type: 'incoming-payment',
actions: ['read', 'complete', 'create']
}
]
}
}
)

console.log(
'\nStep 2: got incoming payment grant for receiving wallet address',
incomingPaymentGrant
)

if (!isFinalizedGrant(incomingPaymentGrant)) {
throw new Error('Expected finalized incoming payment grant')
}

// Step 3: Create the incoming payment. This will be where funds will be received.
const incomingPayment = await client.incomingPayment.create(
{
url: receivingWalletAddress.resourceServer,
accessToken: incomingPaymentGrant.access_token.value
},
{
walletAddress: receivingWalletAddress.id,
incomingAmount: {
assetCode: receivingWalletAddress.assetCode,
assetScale: receivingWalletAddress.assetScale,
value: '1000'
}
}
)

console.log(
'\nStep 3: created incoming payment on receiving wallet address',
incomingPayment
)

// Step 4: Get a quote grant, so we can create a quote on the sending wallet address
const quoteGrant = await client.grant.request(
{
url: sendingWalletAddress.authServer
},
{
access_token: {
access: [
{
type: 'quote',
actions: ['create', 'read']
}
]
}
}
)

if (!isFinalizedGrant(quoteGrant)) {
throw new Error('Expected finalized quote grant')
}

console.log('\nStep 4: got quote grant on sending wallet address', quoteGrant)

// Step 5: Create a quote, this gives an indication of how much it will cost to pay into the incoming payment
const quote = await client.quote.create(
{
url: sendingWalletAddress.resourceServer,
accessToken: quoteGrant.access_token.value
},
{
walletAddress: sendingWalletAddress.id,
receiver: incomingPayment.id,
method: 'ilp'
}
)

console.log('\nStep 5: got quote on sending wallet address', quote)

// Step 7: Start the grant process for the outgoing payments.
// This is an interactive grant: the user (in this case, you) will need to accept the grant by navigating to the outputted link.
const outgoingPaymentGrant = await client.grant.request(
{
url: sendingWalletAddress.authServer
},
{
access_token: {
access: [
{
type: 'outgoing-payment',
actions: ['read', 'create'],
limits: {
debitAmount: {
assetCode: quote.debitAmount.assetCode,
assetScale: quote.debitAmount.assetScale,
value: quote.debitAmount.value
}
},
identifier: sendingWalletAddress.id
}
]
},
interact: {
start: ['redirect']
// finish: {
// method: "redirect",
// // This is where you can (optionally) redirect a user to after going through interaction.
// // Keep in mind, you will need to parse the interact_ref in the resulting interaction URL,
// // and pass it into the grant continuation request.
// uri: "https://example.com",
// nonce: crypto.randomUUID(),
// },
}
}
)

console.log(
'\nStep 7: got pending outgoing payment grant',
outgoingPaymentGrant
)
console.log(
'Please navigate to the following URL, to accept the interaction from the sending wallet:'
)
console.log(outgoingPaymentGrant.interact.redirect)

await readline
.createInterface({ input: process.stdin, output: process.stdout })
.question('\nPlease accept grant and press enter...')

let finalizedOutgoingPaymentGrant

const grantContinuationErrorMessage =
'\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).'

try {
finalizedOutgoingPaymentGrant = await client.grant.continue({
url: outgoingPaymentGrant.continue.uri,
accessToken: outgoingPaymentGrant.continue.access_token.value
})
} catch (err) {
if (err instanceof OpenPaymentsClientError) {
console.log(grantContinuationErrorMessage)
process.exit()
}

throw err
}

if (!isFinalizedGrant(finalizedOutgoingPaymentGrant)) {
console.log(
'There was an error continuing the grant. You probably have not accepted the grant at the url.'
)
process.exit()
}

console.log(
'\nStep 6: got finalized outgoing payment grant',
finalizedOutgoingPaymentGrant
)

// Step 7: Finally, create the outgoing payment on the sending wallet address.
// This will make a payment from the outgoing payment to the incoming one (over ILP)
const outgoingPayment = await client.outgoingPayment.create(
{
url: sendingWalletAddress.resourceServer,
accessToken: finalizedOutgoingPaymentGrant.access_token.value
},
{
walletAddress: sendingWalletAddress.id,
quoteId: quote.id
}
)

console.log(
'\nStep 7: Created outgoing payment. Funds will now move from the outgoing payment to the incoming payment.',
outgoingPayment
)

process.exit()
})()
18 changes: 18 additions & 0 deletions examples/peer-to-peer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "example-peer-to-peer",
"private": "true",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@interledger/open-payments": "workspace:^",
"readline": "^1.3.0"
}
}
13 changes: 13 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
packages:
- 'packages/*'
- 'docs'
- 'examples/*'