Consent Requests
For any recurring payments to be captured from a user, with Capitec Pay, a consent token is required to be created. This instruction must then be authorized by the user, to grant consent for the token to be subsequently charged.
A Capitec Pay recurring payment can be created by creating a Stitch Payment Consent Request with the Capitec Pay method enabled
.
When creating a Payment Consent Request with the Capitec Pay method, detailed information about the customer and the recurring payment is required.
Your customer will be prompted to authorise the recurring payment as per the provided details. Once a recurring payment is successfully
authorised, the Payment Consent Request status will move to GRANTED
, and you will be able to initiate transactions.
Throughout this integration document, the Payment Consent Request
represents the Capitec Pay recurring payment, since
(for the purposes of this guide) Capitec Pay is the only method enabled in the examples.
The below high-level diagram depicts Payment Consent Request creation on the Stitch API. The outcome of the
Payment Consent Request creation is a redirect URL and a consent ID. After authorisation by the customer, the Payment Consent Request will
be in a GRANTED
status. The consent ID can then be used to invoke future payments.
In summary, the Payment Consent Request integration requires:
- Creating the Payment Consent Request,
- Redirecting the customer, and
- Handling the resulting redirect, with webhook.
Create a Payment Consent Request
A Payment Consent Request can be created using the below mutation. Payment Consent Request creation requires a client token with
the client_recurringpaymentconsentrequest
scope.
Follow the steps described in the client token guide to obtain a client token.
To create a Payment Consent Request with Capitec Pay enabled, information about the customer and the collection is required. A redirect URL will be returned for you to surface to your customer, allowing them to provide any outstanding details, and to authorise the recurring payment.
The GraphQL API URL https://api.stitch.money/graphql can be used for all consent requests (whether on test or live clients).
The above mutation specifies a fixed recurring payment. However, a variable payment can be configured by specifying the paymentOptions.variable
property instead.
Once-off payments for fixed or variable amounts are supported. In order to create a consent for a once-off future payment, specify ONCE_OFF
for the paymentSchedule.frequency
input, together with either a fixed or variable amount.
Request Fields
The table below describes the fields that can be populated when creating a payment request.
Name | Description | Type |
---|---|---|
nonce | A nonce is a generated, unique, random value that can only be used once to ensure the uniqueness of the request. | String |
externalReference | Optional value that will be returned in the redirect URL and on queries after the customer grants consent. | String |
paymentOptions | Can be used to define whether the customer will be charged a fixed, or variable recurring amount. | Object |
paymentOptions.fixed | Defines a fixed recurring amount. | Money |
paymentOptions.variable.max | Defines a maximum amount for a variable recurring amount. Displayed to the customer on their mobile banking app. | Money |
paymentSchedule.start | The date the payment consent will be active, and can be charged against. Displayed to the customer on their mobile banking app. | Date (ISO-8601) |
paymentSchedule.frequency | Indicates how often the collection will occur. This can be ONCE_OFF, DAILY, WEEKLY, MONTHLY, or YEARLY. Displayed to the customer on their mobile banking app. | Enum |
paymentDescription | The description displayed to the customer on their banking app. | String |
payerInfo.identifyingDocument | The customer's valid South African identity number, as registered with Capitec. | IdentifyingDocument |
payerInfo.mobileNumber | The customer's mobile number, as registered with Capitec. | String |
payerInfo.bankAccountNumber | The customer's Capitec account number. | String |
To create a Payment Consent Request with Capitec Pay, the user will require one of the following when authorizing:
- South African identity number
- Mobile phone number
- Capitec account number
Temporary residence numbers and passports are not currently supported.
Handle Interaction URL
The redirect URL returned by the API requires that an allowlisted returnUrl
is appended as a query string parameter.
If a customer is redirected to this URL, they will be guided through the process of authorising the consent request. For test
clients, the following URLs are allowlisted 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 allowlisted URL configuration included the URL https://example.com/subscription
,
you'd append the following query parameter to the url
value returned from the
API: &returnUrl=https://example.com/subscription
.
The full URL you expose to the customer would then look like this:
https://secure.stitch.money/v2/consent?requestId=2b068bd5-6a5a-42e1-8a45-673cb3ede612&
clientId=test-195944A9-E957-4532-B574-D37BD5FD9297&
returnUrl=https://example.com/subscription
To add or remove a URL from the allowlist, please reach out to the Stitch team.
Please note that production clients will not be allowed to use unsecure returnUrl
parameters such as http.
When the customer completes or cancels the consent request, they'll be redirected back to the returnUrl
.
The below query string parameters will be passed back to the returnUrl
.
Request Field | Description | Type |
---|---|---|
id | The unique id of this payment consent request. | ID |
status | Status will have the value complete if successful, closed if the customer clicked close in the UI, or failed if something went wrong while authorizing. | String |
externalReference | The externalReference that was used when creating the consent request, if provided. | String |
The id
can be used to retrieve the final consent 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 alter it before redirecting. 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 to verify the signature of each incoming webhook's payload.
Using the webhooks also ensures you'll always be able to know the final status of a payment consent request. If for example the request times out due to a network issue.
Handling Granted Consents
Once a user has granted consent for you to process recurring transactions via Capitec Pay, the consent request will be in a PaymentConsentGranted
state. At this point, you may proceed to initiate transactions using the respective consent ID.
In the case that a subscription has ended, or a user chooses to remove their consent from your platform, you should revoke the consent request, so that it is no longer possible to initiate transactions from their account.
Revoke a Consent Request
A Payment Consent Request can be revoked directly via the Stitch API, or by a customer revoking consent in their banking app. A revocation would result in a Payment Consent Request with a status of PaymentConsentRevoked
. The Payment Consent Request would be inactive and
any payment attempts against it would fail.
In order to revoke a Payment Consent Request, you need to call the paymentConsentRequestRevoke
mutation as detailed below:
The table below describes the fields detailed in the above mutation.
Name | Description | Type |
---|---|---|
id | The unique id of the original payment consent request ID as returned by the Stitch API when you create a payment consent request. | ID |
reason | The reason for revoking the payment consent request. The revocation reason supplied needs to be one of the reasons as indicated in the "Reason" column below. | String |
Consent Request Revocation Reasons
Reason | Description |
---|---|
CONTRACT_TERMINATED | The contract has been terminated. |
FRAUD | Revoked due to fraud related activity by the customer. |
GENERAL | General cancellation that isn't for one of the aforementioned reasons. |
A Payment Consent Request can only move to a PaymentConsentRevoked
status from a PaymentConsentGranted
status.
Cancel a Consent Request
A Payment Consent Request that has been created, and is still pending, can be cancelled in order to prevent a user from granting their consent on the current request. This can be done by executing the paymentConsentRequestCancel
mutation with a consent ID and a reason.
This is different to revocation in that a user has not previously granted consent to the recurring payment. See the status descriptions in the table below for more information.
An example of a cancellation mutation is shown below:
Consent Request Statuses
Payment Consent Request statuses can be queried directly or obtained via a subscription to the webhooks.
Status | Description |
---|---|
PaymentConsentPending | The initial status after creation via the API. |
PaymentConsentGranted | The consent request was authorised by the customer, and can be charged against. |
PaymentConsentFailed | The consent request initiation failed. This is usually due to rejection or error by a downstream provider. Further failure codes and descriptions are included with this status. |
PaymentConsentCancelled | The consent request has been cancelled while it was still pending, before it was authorised. |
PaymentConsentExpired | The consent request has expired while it was still pending, before it was authorised. |
PaymentConsentRevoked | The consent request was revoked and cannot be charged against. |
While we encourage the use of webhooks for obtaining status updates, as they are immediate and don't require polling, the following query is an example of fetching a payment consent request by ID to obtain the status.
Below is a diagram depicting the possible Payment Consent Request Statuses
Subscribe to Webhooks
Webhooks for updates on consent requests can be subscribed to by running the following mutation:
If the webhook subscription is successfully created, the body returned by the request will look similar to 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-consent-request
webhook will be dispatched for each of the following status updates:
PaymentConsentProcessing
PaymentConsentFailed
PaymentConsentGranted
PaymentConsentCancelled
PaymentConsentExpired
PaymentConsentRevoked
Example CapitecPay VRP Payload
{
"data": {
"createdAt": "2023-06-11T11:52:49.230Z",
"id": "cmVjdXJyaW5nUGF5bWVudENvbnNlbnRSZXF1ZXN0L2NiNmYyYmQ5LTVjODgtNDFmOS04MmE1LWE0NWEyNDYxMGJmNg==",
"status": "granted",
"type": "capitecpay",
"updatedAt": "2023-06-21T11:52:49.230Z"
},
"datetime": "2023-05-10T12:22:42.865Z",
"id": "consent-request:status:success:cb6f2bd9-5c88-41f9-82a5-a45a24610bf6",
"type": "consent-request-status"
}
Testing and Simulation
When using a test client, the default flow will result in a GRANTED
payment consent request, and the default transaction result will be successful. However, it is possible to simulate other outcomes.
Simulated Statuses
Payment consent request scenarios can be simulated by configuring the paymentDescription
when creating the payment consent request.
paymentDescription value | Scenario | Consent status outcome |
---|---|---|
success | Simulates the user accepting the recurring payment in-app prompt. This is the default successful scenario, the same as if the field was left empty. | GRANTED |
timeout | Simulates the user not responding to in-app prompt before the request expires. The user may be able to retry. | PENDING |
declined | Simulates the user declining the in-app prompt on their device. | PENDING |
maxTransactionLimitExceeded | Simulates the user not being able to authorize the consent due to Capitec maximum transaction amount limit. | FAILED |
clientTransactionLimitExceeded | Simulates the user not being able to authorize the consent due to transaction limits set in the Capitec app. | PENDING |
clientAppNotFound | Simulates the user not being able to auhtorize the consent due to not having the Capitec app installed. | PENDING |
clientDeactivated | Simulates the user not being able to authorize the consent due to a deactivated Capitec account. | PENDING |
clientBlockedMerch | Simulates the user not being able to authorize the consent due to blocking the merchant on the Capitec app. | PENDING |
clientNotFound | Simulates the user not entering a valid identifier for a Capitec account. | PENDING |
duplicateConsent | Simulates the user not being able to authorize the consent due to an existing duplicate consent that has already been authorized | FAILED |