LinkPay Integration Process
Integration Process Summary
- Get a client token
- Generate the payment authorization request url
- Surface the url to the user using the user authorization flow
- Get a user token
- Initiate a payment using a user token
- Handle the required user interaction during payment initiation
- Handle the final payment initiation response from the API, and query the status and/or optionally subscribe to receive a webhook upon payment completion
When integrating LinkPay, a payment authorization request represents a resource created that allows users to authorize and set up payments to a specific beneficiary.
A client token is needed to create a payment authorization request. To obtain a LinkPay client token, follow the client authentication guide,
making sure to request the client_paymentauthorizationrequest
scope.
Once the authorization request has been completed, you can use the authorization code to obtain a user token by following
the user authentication guide. This token can be used to initiate payments
at any time, using the userInitiatePayment
mutation on the Stitch API. If no user interaction is required, the payment
will complete instantly.
However, if an interaction such as multifactor authentication is necessary, then the user may be presented with a web interface within your application, allowing them to complete the payment after supplying the required input.
Postman Collection
The LinkPay integration process has been demonstrated in this Postman collection. Please import and review the collection's documentation, supplying the variables where required.
Creating an Account Linking Request
Once you have obtained a client token, you are able to use it to create an account linking request. An example mutation demonstrating the creation of the account linking request is shown below.
Note that the two references provided on payer
and beneficiary
reflect the reference that will appear on the payer and beneficiary's statement respectively.
The payer field requires that email and contact number are provided.
Retrieving a Linked Account Token
The result of the above mutation returns the authorizationRequestUrl
, which is the URL that replaces the base
https://secure.stitch.money/connect/authorize
URL used in the user authorization flow.
In order to retrieve a user token, you will need to obtain an authorization code by presenting the user with a fully built authorize URL and handling the callback.
Below is a quick look at what you would need to build and append to the URL returned by the above call. Note that the
client_id
and redirect_uri
values would need to be replaced with your own. You can find out more on how to go about
building this URL here.
?client_id=YOUR_CLIENT_ID_HERE&scope=openid%20transactions%20accounts%20balances%20accountholders%20paymentinitiationrequest%20offline_access&response_type=code&redirect_uri=http://localhost:8080/return&state=J_Ug3dCQJG61Tt2Sejt3TY5ExgqemTXpEFhyFGapXwA&nonce=fim8Mwz0yHE85NOfgMD_sk3MdKzq2vQ_QnC0KUxIGT4&code_challenge=QO6cTsxK-_FWDrJ9xgg1fvTzsgKBcGZWHLhddFoc1tU&code_challenge_method=S256
Retrieving Data of a Linked Account
When storing the user tokens obtained in the account linking step, you will need
to also store the account information related to that token. For this you can query the paymentAuthorization
on the user
field.
If the user has not immediately prior completed a linking and a session is not already active then this may trigger a login notification from the user's banking app.
If the session is not active you may receive a REAUTHORIZATION_REQUIRED
response. In this case you may ask the user to
login again by redirecting them to the URL in the response, as per the reauthorization guide.
Alternatively, you could simply attempt to initiate a payment and handle any USER_INTERACTION_REQUIRED
response, which
will subsequently activate the session again.
To obtain identity information about the user that has just linked their account and verify it against your own user KYC
information, you can use the below query. Including the accountHolder
field fetches the user's identity information
Initiate Payment
Once you have obtained a user token, you can use the token to initiate a payment. An example query that initiates a payment
can be seen below. The externalReference
field can be used to correlate transaction IDs within your system with the payments
initiated by Stitch.
Please note that:
- The
payerReference
field is restricted to a maximum of 12 characters. - The
beneficiaryReference
field is restricted to a maximum of 20 characters. - The
externalReference
field is restricted to a maximum of 4096 characters.
Payment Initiation Statuses
This table describes the different statuses a payment initiation might have:
Response | Status | Description |
---|---|---|
PaymentInitiationCompleted | PaymentInitiationCompleted | This is a final payment state, indicating successful movement of funds. |
USER_INTERACTION_REQUIRED | PaymentInitiationPending | The bank requires the user to complete a manual action. More explanation on how to handle this here |
PAYMENT_FAILED | PaymentInitiationFailed | This is a final payment state indicating that the payment failed for one of the reasons explained here |
PaymentInitiationExpired | PaymentInitiationExpired | The payment has expired while awaiting user interaction. More information on payment initiation expiry can be found here |
Handling User Interaction Required
Similar to card, with 3D Secure, there are instances where a payment cannot complete without user interaction. This might be because the bank requires that a multifactor challenge be completed before the payment is processed, or that the user needs to accept a prompt, such as updated terms and conditions, on their account.
In this case, the payment initiation mutation will return an error, as well as a payment URL. The user will need to be
redirected to this URL in order to complete the payment. The URL returned by the API requires that a whitelisted redirect_uri
is appended to it as a query string argument. An example of this response can be found below:
Link the test data Capitec account to simulate this response when using a test client.
{
"errors": [
{
"message": "Multifactor Required to continue payment.",
"extensions": {
"code": "USER_INTERACTION_REQUIRED",
"userInteractionUrl": "https://secure.stitch.money/connect/payment-request/3991a32d-6d82-467e-ae47-5345022adc63",
"id": "cGF5aW5pdC8zOTkxYTMyZC02ZDgyLTQ2N2UtYWU0Ny01MzQ1MDIyYWRjNjM="
},
"locations": [
{
"line": 4,
"column": 23
}
],
"path": ["userInitiatePayment"]
}
],
"data": {
"__typename": "Mutation",
"userInitiatePayment": null
}
}
Handling Payment Failed Errors
An example of a failed payment response can be found below:
{
"errors": [
{
"message": "Payment failed because insufficient funds were available.",
"locations": [
{
"line": 4,
"column": 23
}
],
"path": ["userInitiatePayment"],
"extensions": {
"code": "PAYMENT_FAILED",
"reason": "insufficient_funds"
}
}
],
"data": {
"__typename": "Mutation",
"userInitiatePayment": null
}
}
Possible Payment Errors
This table describes the reasons you may encounter within the PAYMENT_FAILED
error:
Value | Description |
---|---|
bank_error | Payment failed because a bank error has occurred. |
blocked | Payment failed due to blocked account. |
declined | Payment has been declined. |
duplicate_payment | Payment failed because a duplicate payment has already occurred. |
invalid_destination_account | Payment failed due to an invalid destination account. |
invalid_source_account | Payment failed due to an invalid source account. |
limits_exceeded | Payment failed due to account limits exceeded. |
limits_not_met | Payment failed due to payment minimum not met. |
readonly_account_error | Payment failed as this account is read only. |
timeout | Payment failed due to a timeout. |
payment_pending | Error marking payment as complete. Please contact the Stitch team if you encounter this error. |
unknown | Payment failed due to an unknown error. |
Surface URL and Handle Callback
The URL returned by the API requires that a whitelisted redirect_uri
be 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 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
To add or remove a URL from the whitelist, please reach out to a Stitch Engineer via the usual support channels on Slack or our Support Form
Please note that production clients will not be allowed to use unsecure redirect_uri
params such as http
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 | Status will have the value "complete" if successful, "closed" if the user clicked close in the UI, or "failed" if something went wrong when attempting the payment. | String |
externalReference | The value that was provided to the externalReference field when the payment initiation request was created. | 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 "completed":
redirect("/eft-success");
case "closed":
case "failed":
redirect("/eft-retry");
default:
break;
}
Expiring Pending Payment Initiations
An optional expireAt
Date (ISO 8601) can be supplied in the creation of the payment initiation. This can be used to mark
the payment as expired if the payment is not immediately successful, and requires user-interaction. However, if the payment
multifactor authentication is already underway then the payment can still be completed by the user.
Cancelling Pending Payment Initiations
If user interaction is required or the user clicks the X
on the dialog box, the payment initiation will remain in the
PaymentInitiationPending
state until the user completes the required interaction, the payment initiation has been cancelled
or the expireAt
Date has passed if expireAt
was supplied in the payment initiation creation request.
Cancelling a payment initiation will result in the PaymentInitiationCancelled
state and can be done by calling the
clientPaymentInitiationCancel
mutation, which also triggers the cancel webhook event.
Subscribing to LinkPay Webhooks
To receive a webhook upon payment completion or failure you will need to create a subscription for your client. Please see the example below to set up a basic unsigned webhook.
Please note that unlike the example shown on the Webhooks page that uses the paymentInitiationRequest
node,
to receive LinkPay webhooks, one should subscribe to the paymentInitiation
node.
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.
Viewing all Initiated Payments
To view the collection of initiated payments, you may query the Client.paymentInitiations
field on the API.
This will return a paged collection of initiations, dated from most recent to oldest.
Retrieving Payment Initiation Status
When a user has successfully initiated the payment, the status will be PaymentInitiationCompleted
and contain details
of the payer and the date it was completed.
To retrieve the status of a payment initiation, you'll need a client token with the client_paymentauthorizationrequest
scope.