Building a Web3 Peer to Peer Payments App
Conceptual Overview
By combining the power of many of Wyre's APIs, our partners can build apps that allow end users to send and receive funds similar to many of the popular peer-to-peer payments apps that have become household names.
With Wyre's Users API, we allow partners and developers to safely and compliantly KYC and onboard their users as well as store funds for users that fulfill KYC requirements.
Wyre's Payment Method API allows bank accounts to be attached to every user that passes KYC.
Wyre's Transfers API enables fiat funds onboarding via various banking methods as well as sending payments to other users or exchanging currencies between users or within the same user without a blockchain network fee.
Step 1: Create a Wyre Account and Generate Keys
Go to Set Up Your Account to learn how to generate keys and start building on TestWyre!
Step 2: Create a User
Create a User, first and last name recommended as default.
{
"fields": {
"firstName": "Alberta",
"lastName": "Charleson"
},
"blockchains": [
"ALL"
]
}
{
"id": "US_39CUCTGTVBB",
"status": "APPROVED",
"partnerId": "PT_UUEJLH74VEN",
"type": "INDIVIDUAL",
"createdAt": 1648361213033,
"depositAddresses": {
"BTC": "miFkpeaFfrLG9A7owsHWQnj8iXitsdB4Gk",
"MATIC": "0xf40501f440eb0c596ee053f88bd43ba9ae136b56",
"AVAX": "0xe494a6d8d10f23630ba9f0b99bf455e9d4951abc",
"ETH": "0x6c70a5acb186d4c447719d24e31a2a80f1539dc1",
"XLM": "GD7WXI7AOAK2CIPZVBEFYLS2NQZI2J4WN4HFYQQ4A2OMFVWGWAL3IW7K:V9PMQ8EYEWC",
"AVAXC": "0xf01724bb979df8ff888805809a014d93087f8d24"
},
"totalBalances": {},
"availableBalances": {},
"fields": {
"firstName": {
"value": "Alberta",
"error": null,
"status": "SUBMITTED"
},
"lastName": {
"value": "Charleson",
"error": null,
"status": "SUBMITTED"
},
"dateOfBirth": {
"value": "",
"error": null,
"status": "OPEN"
},
"residenceAddress": {
"value": {""
},
"error": null,
"status": "OPEN"
}
}
}
Step 3: Generate Onboarding Url
Generate an Get KYC Onboarding URL to fully KYC the User.
curl --request GET \
--url https://api.testwyre.com/v3/users/US_X3RCVXREFCA/onboarding \
--header 'Accept: application/json' \
--header 'Authorization: Bearer null'
{
"onboardingUrl": "https://pay.testwyre.com/users/US_X3RCVXREFCA/onboarding/TYAQL6BPRNQCJVQWZNU2CQUD6JTT4ATF49WZJXCHYB6XNR2LU9"
}
Step: 4 Verify User Status
Set up User webhooks to receive automated updates when a User status changes.
{
"userId":"US_9UG6JF2Y26T",
"type": "USER_STATUS_UPDATED"
}
Use the webhook to trigger a query of the User status with Get User. Make sure to use the 'scopes' Url parameter to check the status of the particular functionality your User is attempting to get approved for.
curl --location --request GET 'https://api.testwyre.com/v3/users/US_9UG6JF2Y26T?scopes=ACH' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ' \
--data-raw ''
Validate that the status is 'Approved'
{
"id": "US_NDTJFWMRH3G",
"status": "APPROVED",
"partnerId": "PT_6NU9NYY3XRU",
"type": "INDIVIDUAL",
"createdAt": 1653678433000,
"depositAddresses": {
"BTC": "mzq6mcgyNvREve5ofEp1tmtBTD8h9Rzxm5",
"MATIC": "0x0a8040e58b947d30a3f09fd4cb17c94a969054ec",
"AVAX": "X-fuji1afarwjk5srh6ckhyy03ptyxrdkj602dhglznlx",
"ETH": "0x7107cee70690c5c9b18f58500e1f27e3c7640640",
"XLM": "GD7WXI7AOAK2CIPZVBEFYLS2NQZI2J4WN4HFYQQ4A2OMFVWGWAL3IW7K:R6TCNAJ8GHE",
"AVAXC": "0xb5599a7ce6b82858c6edc25725d7fe49262c5811"
},
"totalBalances": {},
"availableBalances": {},
"fields": {
"legalName": {
"value": "Tim Cooke",
"error": null,
"status": "SUBMITTED"
},
"cellphoneNumber": {
"value": "+14154567895",
"error": null,
"status": "SUBMITTED"
},
"email": {
"value": "[email protected]",
"error": null,
"status": "SUBMITTED"
},
"residenceAddress": {
"value": {
"street1": "342 California St",
"city": "san francisco",
"state": "CA",
"postalCode": "95115",
"country": "US"
},
"error": null,
"status": "SUBMITTED"
},
"ssn": {
"value": "REDACTED",
"error": null,
"status": "SUBMITTED"
}
}
}
Step 5: Connect Bank Account
By masquerading as the user, we can attach a payment method using our Create Payment Method API via two methods:
The first method is creating a Plaid processor token and then passing it to Wyre via the API.
Plaid Required for ACH Onramp
For chargeable ACH payment methods it is required to link the bank account using Plaid.
#first create processor token via plaid
curl --location --request POST 'https://sandbox.plaid.com/processor/token/create' \
--header 'Content-Type: application/json' \
--data-raw '{
"client_id": "60f5d817da3e5300110xxxx",
"secret": "a0adfa408349072862bbb7xxxxxx",
"access_token": "access-sandbox-bd8a8917-84bd-4d1a-b231-671ecf3fb850",
"account_id": "DmzZEm5kA7Hl3yepvXN4hQ4lj6yrGXhXVPNKk",
"processor": "wyre"
}'
#response:
{
"processor_token": "processor-sandbox-af653618-f9e4-41ad-a567-03109952b2b3",
"request_id": "GssgMK1wnpPBeiK"
}
#Take the processor token from the response and send it to create payment method API
#masquerading as the User
curl --location --request POST 'https://api.testwyre.com/v2/paymentMethods?masqueradeAs=user:US_39CUCTGTVBB' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-XXX' \
--header 'Content-Type: application/json' \
--data-raw '{
"plaidProcessorToken": "processor-sandbox-af653618-f9e4-41ad-a567-03109952b2b3",
"paymentMethodType": "LOCAL_TRANSFER",
"country": "US"
}'
#response:
{
"id": "PA_DZ9WJXQEPXB",
"owner": "user:US_39CUCTGTVBB",
"createdAt": 1648364651822,
"name": "Plaid Checking 0000",
"defaultCurrency": "USD",
"fingerprint": "EH6HMC/UfK+45MdA9s+RlVE3mzbROARQiT5KGh26c94=",
"status": "PENDING",
"statusMessage": null,
"waitingPrompts": [],
"linkType": "LOCAL_TRANSFER",
"beneficiaryType": "UNKNOWN",
"supportsDeposit": true,
"nameOnMethod": null,
"last4Digits": "0000",
"brand": null,
"expirationDisplay": null,
"countryCode": "US",
"nickname": null,
"rejectionMessage": null,
"disabled": false,
"supportsPayment": true,
"chargeableCurrencies": [
"USD"
],
"depositableCurrencies": [
"USD"
],
"chargeFeeSchedule": null,
"depositFeeSchedule": null,
"minCharge": null,
"maxCharge": null,
"minDeposit": null,
"maxDeposit": null,
"documents": [],
"blockchains": {},
"liquidationBalances": {},
"srn": "paymentmethod:PA_DZ9WJXQEPXB"
}
Offramp Only ACH
For non-chargebable ACH payment methods (chargeablePM = false), as an alternative to Plaid you can connect a bank account by passing in bank and beneficiary information.
## leave paymentMethodType as INTERNATIONAL_TRANSFER regardless of users location
curl --location --request POST 'https://api.testwyre.com/v2/paymentMethods?masqueradeAs=user:US_39CUCTGTVBB' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-xxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"paymentMethodType": "INTERNATIONAL_TRANSFER",
"paymentType": "LOCAL_BANK_WIRE",
"currency": "USD",
"country": "US",
"beneficiaryType": "INDIVIDUAL",
"firstNameOnAccount": "Alberta",
"lastNameOnAccount": "Charleson",
"beneficiaryAddress": "2992 Cameron Road",
"beneficiaryAddress2": "",
"beneficiaryCity": "Malakoff",
"beneficiaryPostal": "14236",
"beneficiaryPhoneNumber": "+15555555555",
"beneficaryState": "NY",
"beneficiaryDobDay": 2,
"beneficiaryDobMonth": 12,
"beneficiaryDobYear": 1990,
"accountNumber": "1234567890123",
"routingNumber": "123412312",
"accountType": "CHECKING",
"chargeablePM": false
}'
#response:
{
"id": "PA_PTXN82CCWRH",
"owner": "user:US_39CUCTGTVBB",
"createdAt": 1648783498152,
"name": "USD Bank account ending in 0123",
"defaultCurrency": "USD",
"fingerprint": "BANK-bZwB7bB30jWoskzil4q13Vd9oxqn4g3mLhdUGd+dDIA=",
"status": "AWAITING_FOLLOWUP",
"statusMessage": null,
"waitingPrompts": [
{
"id": "XGBPVZFYHYY",
"prompt": "Upload a bank statement",
"responses": [],
"type": "DOCUMENT"
}
],
"linkType": "INTERNATIONAL_TRANSFER",
"beneficiaryType": "INDIVIDUAL",
"supportsDeposit": true,
"nameOnMethod": "Alberta Charleson",
"last4Digits": "0123",
"brand": null,
"expirationDisplay": null,
"countryCode": "US",
"nickname": null,
"rejectionMessage": null,
"disabled": false,
"supportsPayment": false,
"chargeableCurrencies": [],
"depositableCurrencies": [
"USD"
],
"chargeFeeSchedule": null,
"depositFeeSchedule": null,
"minCharge": null,
"maxCharge": null,
"minDeposit": null,
"maxDeposit": null,
"documents": [],
"blockchains": {},
"liquidationBalances": {},
"srn": "paymentmethod:PA_PTXN82CCWRH"
}
Step 6: Create a User Wallet
Use Wallets with masquerading to assign the wallet to the user.
curl --location --request POST 'https://api.testwyre.com/v2/wallets?masqueradeAs=user:US_CELH74LZG4A' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ' \
--data-raw '{
"name": "wallet 1 US_CELH74LZG4A",
"callbackUrl": "https://requestinspector.com/inspect",
"notes": "Notes about the wallet"
}'
{
"pusherChannel": "2c6ed35b5cbb4e7c7478b0238a7d26a1",
"balances": {},
"srn": "wallet:WA_N2B76P6BZ8U",
"callbackUrl": "https://requestinspector.com/inspect",
"notes": "Notes about the sub account",
"depositAddresses": {
"BTC": "mtm3sMtYU499d6yURr86g48CBtyDkTh7ho",
"MATIC": "0x5e364e231a79ff0da9d3150239afb41d4c6295c7",
"AVAX": "X-fuji1lh4qjuc9a206ju5neezuw8hqfvmwq2lls90h0p",
"ETH": "0x275b668bdbba8d1285feb7c3861f3bea22b31e9f",
"XLM": "GD7WXI7AOAK2CIPZVBEFYLS2NQZI2J4WN4HFYQQ4A2OMFVWGWAL3IW7K:WTE3NZUYW8P",
"AVAXC": "0x5a66421bd1856b375c823cd299b36f9425f700f5"
},
"totalBalances": {},
"availableBalances": {},
"savingsReferralSRN": null,
"name": "wallet 1 US_LWYHFQTFTZJ",
"id": "WA_N2B76P6BZ8U",
"type": "DEFAULT",
"status": null
}
Step 7: Onramp User Funds to Wallet
You can pull funds into Wyre using a payment method as the source.
curl --location --request POST 'https://api.testwyre.com/v3/transfers' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-xxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"source": "paymentmethod:PA_DZ9WJXQEPXB",
"sourceCurrency": "USD",
"sourceAmount": "200",
"dest": "wallet:WA_N2B76P6BZ8U",
"destCurrency": "BTC",
"message": "buying btc",
"autoConfirm": true
}'
Step 8: Move Funds around Wyre
Through Wyre's Transfers API you can move and exchange funds in and out of the platform. Common transfer types include user to user transfers, crypto exchange and offramp bank to the user's payment method.
# a simple fiat to fiat transfer similar to a cash app/venmo payment
curl --location --request POST 'https://api.testwyre.com/v3/transfers' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-xxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"source": "wallet:US_39CUCTGTVBB",
"sourceCurrency": "USD",
"sourceAmount": "20",
"dest": "wallet:WA_648UELU24CQ",
"destCurrency": "USD",
"message": "Sending USD to WA_648UELU24CQ",
"autoConfirm": true
}'
# doing a currency exchange for a user
curl --location --request POST 'https://api.testwyre.com/v3/transfers' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-xxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"source": "wallet:WA_N2B76P6BZ8U",
"sourceCurrency": "ETH",
"sourceAmount": "0.01",
"dest": "wallet:WA_N2B76P6BZ8U",
"destCurrency": "BTC",
"message": "converting eth to BTC for user:US_39CUCTGTVBB",
"autoConfirm": true
}'
# paying out to bank account
curl --location --request POST 'https://api.testwyre.com/v3/transfers' \
--header 'Authorization: Bearer SK-86Z8FYFB-UTA2RDCN-HCZBFT2V-xxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"source": "wallet:WA_N2B76P6BZ8U",
"sourceCurrency": "USD",
"sourceAmount": "20",
"dest": "paymentmethod:PA_DZ9WJXQEPXB",
"destCurrency": "USD",
"message": "sending USD to bank account",
"autoConfirm": true
}'
Step 9: Add Blockchains (Optional)
For Algorand and Flow addresses you must opt in to generate a wallet address, use the Wallets attach endpoint for this.
curl --location --request POST 'https://api.testwyre.com/v3/blockchain/attach?masqueradeAs=user:US_LWYHFQTFTZJ' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ' \
--data-raw '{
"targetSrn": "wallet:WA_N2B76P6BZ8U",
"blockchain": "ALGO"
}'
{
"ALGO": "OW5S25V7DEK7EXMZU2E6F26SYMM32JGXV2A23MGIH3E3IO2ACJLH4TS53Q"
}
Updated about 2 years ago