Pay with Crypto Integration Process
Crypto payments can be made easily by creating payment requests via the API. Users can then be guided to the returned redirect URLs, for them to complete their card payments within the Stitch-hosted UI.
Generate Payment Request URL
Much like Pay By Bank, crypto payment creation is protected by a client token. You'll need to follow the steps described in the client token guide
to obtain a client token with the client_paymentrequest
scope. To create a payment initiation request, you'll need to
ensure that the feature is enabled on your client.
To create the request, a GraphQL mutation is used to specify the requested amount, references and merchant information.
The GraphQL API URL https://api.stitch.money/graphql can be used for all payment requests (whether on test or live clients).
An example of a GraphQL request to create a payment initiation request with the crypto payment method is shown below:
The following table outlines the possible inputs for Crypto payments:
Status | Description |
---|---|
enabled | Boolean field used to specify enable or disable Crypto payments. Note that by default, this method is disabled. |
Please note that:
- The
paymentMethods.crypto
type requires client configuration. Contact the technical team to assist in enabling your client for this feature. - The amount that the user should pay to you should still be specified in the fiat currency (such as ZAR). Stitch will handle the conversion from cryptocurrency to fiat.
Expiring payment requests
It is highly recommended that an expireAt
Date (ISO 8601) is supplied in the creation of any payment initiation request. At the specified
date and time, the payment request status will automatically move to PaymentInitiationRequestExpired
, if the payment is not yet successfully completed.
Surface URL and Handle Callback
The URL returned by the API requires that a whitelisted redirect_uri
is appended to it as a query string argument. If
you direct a user to this URL, they will be guided through the process of completing the payment. For test clients, we
do have the following URLs whitelisted by default:
- https://localhost:8080
- https://localhost:8080/return
- https://localhost:3000
- https://localhost:3000/return
- https://localhost:9000
- https://localhost:9000/return
For example, if your whitelist URL configuration included the URL https://example.com/payment
for payment requests, you'd
append the following query string to the url
returned from the API: ?redirect_uri=https%3A%2F%2Fexample.com%2Fpayment
.
The full URL you expose to the user should look like this
https://secure.stitch.money/connect/payment-request/2b068bd5-6a5a-42e1-8a45-673cb3ede612?redirect_uri=https%3A%2F%2Fexample.com%2Fpayment
To add or remove a URL from the whitelist, please reach out to a Stitch Engineer via our Support Form
Please note that production clients will not be allowed to use unsecure redirect_uri
params such as http. For example
Once the user completes or cancels the payment request, they'll be redirected back to the redirect_uri
.
The below query string parameters will be passed back to the redirect_uri.
Request Field | Description | Type |
---|---|---|
id | The unique id of this payment request | ID |
status | The status will have the value complete if successfully paid with crypto, closed if the user chooses to close the UI, or failed if something went wrong when generating the crypto payment details | String |
payment_method | The method used to complete the payment, which would be crypto in this case | String |
externalReference | The value that was provided to the externalReference field when the payment request was created. It can be used to correlate transaction IDs within your system with the payment request initiated by Stitch | String |
The id
can be used to retrieve the final payment request status and other details from the Stitch API, as well as relate to incoming webhooks.
The status
field should not be used for any database operations since an external party can tweak it. To secure yourself from
attackers who can send a fake payload to your redirect or webhook endpoint, we recommend:
- using the webhooks as the source of truth as they are secure and will always be sent from Stitch.
- making use of signed webhooks since you'll be able to verify the signature of each incoming webhook's request payload.
Using the webhooks also ensures you'll still be able to know the final status of a payment request if for example the request times out due to a network issue.
Using Multiple Internal Redirect URIs
For a situation where different callbacks lead to different internal URLs, you SHOULD have a single whitelisted redirect URL which can then have the logic handling the Stitch callback and redirect to the internal URLs. An overview of how to do this in NodeJS is as shown below:
const status = params.status;
switch (status) {
case "complete":
redirect("/crypto-success");
case "closed":
redirect("/crypto-processing");
case "failed":
redirect("/crypto-retry");
default:
break;
}
Note that if an expiry time is provided, this needs to be at least one hour from payment creation, in the case of crypto payments. This is due to a user being able to fulfil their crypto payment within the Stitch UI,
Cancelling Pending Payment Initiation Requests
If the user clicks the X
on the dialog box, the payment initiation request will remain in the PaymentInitiationRequestPending
status. To cancel the payment request, you should call the clientPaymentInitiationRequestCancel
mutation, which also
triggers the cancel webhook event.
Payment Initiation Request Statuses
The table below describes the different statuses a Crypto request can have, with the initial status always being PaymentInitiationRequestPending
:
Status | Description |
---|---|
PaymentInitiationRequestCompleted | This is a final payment state. |
PaymentInitiationRequestPending | The user hasn't yet completed the payment initiation request, or they exited the Stitch dialog box before completing the bank selection process. |
PaymentInitiationRequestCancelled | The payment initiation request was manually cancelled by the client. More information on this can be found here. |
PaymentInitiationRequestExpired | The payment initiation request has expired while awaiting user interaction. More information on this can be found here. |
Subscribe to Webhooks
Since Crypto payments are made similarly to Pay By Bank, we subscribe to webhooks and receive payment updates in the same way.
If the subscription is successfully created, the body returned by the request will look like the sample in the Example Response
tab in widget above.
For more information on receiving webhook events, listing active webhook subscriptions, unsubscribing from webhooks and validating signed webhook subscriptions, please visit the Webhooks page.
Webhook Statuses
The payment
webhook will be dispatched for each of the following status updates:
PaymentInitiationRequestCompleted
PaymentInitiationRequestCancelled
PaymentInitiationRequestExpired
Example Payload
{
"data": {
"client": {
"paymentInitiationRequests": {
"node": {
"__typename": "PaymentInitiationRequest",
"amount": {
"currency": "ZAR",
"quantity": "1"
},
"bankBeneficiaries": [
{
"__typename": "BankBeneficiary",
"accountNumber": "1223273660",
"bankAccountNumber": "1223273660",
"bankId": "nedbank",
"name": "FizzBuzz Co."
}
],
"beneficiaries": [
{
"__typename": "BankBeneficiary",
"accountNumber": "1223273660",
"bankAccountNumber": "1223273660",
"bankId": "nedbank",
"name": "FizzBuzz Co."
}
],
"beneficiaryReference": "KombuchaFizz",
"cancellationReason": null,
"created": "2022-10-11T09:52:13.312Z",
"createdAt": "2022-10-11T09:52:13.312Z",
"currency": "ZAR",
"events": [],
"externalReference": "79261d16-c53b-48eb-9019-dc9cfb6c5126",
"failureReason": null,
"id": "cGF5cmVxLzk2YjUyODU1LTBkMzQtNDI0MS04YmM2LWE4ODBlMDQ1ZGQzOQ==",
"payerConstraints": null,
"payerReference": "Joe-Fizz-01",
"paymentConfirmation": {
"__typename": "PaymentPending",
"date": "2022-10-11T09:52:53.128Z"
},
"paymentMethods": [
{
"cash": {
"atm": {
"enabled": true
},
"retailer": {
"barcodeNumber": "test-11132098754321",
"barcodeUrl": "https://test-barcode-url.com",
"enabled": true
}
}
},
{
"eft": {
"enabled": true
}
}
],
"paymentRequestPayerConstraintRelationship": null,
"quantity": "1",
"refunds": [],
"state": {
"__typename": "PaymentInitiationRequestCompleted",
"amount": {
"currency": "ZAR",
"quantity": 1
},
"beneficiary": {
"__typename": "BankBeneficiary",
"accountNumber": "1223273660",
"bankAccountNumber": "1223273660",
"bankId": "nedbank",
"name": "FizzBuzz Co."
},
"date": "2022-10-11T09:52:53.114Z",
"id": "96b52855-0d34-4241-8bc6-a880e045dd39",
"payer": {
"__typename": "PaymentInitiationBankAccountPayer",
"accountName": "Current account",
"accountNumber": "4104754941",
"accountType": "current",
"bankId": "absa"
},
"proofOfPayment": null
},
"updated": "2022-10-11T09:52:53.128Z",
"updatedAt": "2022-10-11T09:52:53.128Z",
"url": "https://secure-local.stitchmoney.com/connect/payment-request/96b52855-0d34-4241-8bc6-a880e045dd39",
"userReference": "Joe-Fizz-01"
}
}
}
}
}
Retrieving Payment Request Status
The status of a Crypto request works much like Pay By Bank.
To determine if and how a payment request was completed, we will need to retrieve its status.
The two pieces of information you need from the response at this stage are the payment request id
, and the url
.
The payment request id
is used to correlate responses and to look up the status of the request in the API, and so
should be retained for later usage. The url
is used to enable the user to authorize the payment request, and you'll
need to redirect the user to this URL. We'll cover this in the next section.
Integrating Using an Iframe
If you are integrating SafeLink into your web application using an iframe,
you will need to configure the allow
attribute
to enable users to copy the deposit details for easy input into their chosen cryptocurrency app.
The allow
attribute should be set as follows:
<iframe
src="https://secure.stitch.money/connect/payment-request/0957174c-a600-4f7a-ac81-21a696338b0c?redirect_uri=https%3A%2F%2Fexample.com%2Fpayment"
allow="clipboard-write https://secure.stitch.money"
></iframe>
Simulating Completed Payment Requests
When using your test client, you may simulate completion of a payment request, directly over the API. This replicates payment updates that would be made when a real crypto payment is completed on a live client.
This is done by running the mutation shown below, containing the following input values:
- The
paymentRequestId
of the pending payment request to be completed, and - The
paymentMethod
specified with the valuecrypto
.
This results in the payment request moving to the final status PaymentInitiationRequestCompleted
.
A corresponding webhook notification will also be sent to your configured receiving endpoint.
Simulating the different statuses for Payment Requests
For testing purposes, certain amounts can be specified as part of the payment request to simulate different scenarios. The table below shows the different amounts and status mappings:
Amount | Status |
---|---|
21 | COMPLETE (User pays the correct amount) |
- This only applies to test clients.
- These status changes are triggered 30 seconds after the request is made.
Postman Collection
Crypto requests can be created and tested using the Postman collection available here. These can be used by specifying your client credentials and supplying the custom request variables where required.