Variable Payment Consents
For any variable recurring payments to be captured from a user, a consent must first be created and then authorised by the user, in order for it to be subsequently charged.
Based on your use-case, you may require to initiate a variable once-off, or variable recurring consent request.
Consent Request Flow
A typical flow would involve the following steps:
- A consent is created, with a specified frequency (such as monthly, or once-off), corresponding amount details, and user details.
- The user is redirected to Stitch to initialise the consent request, and then approves bank authorization for the payment instruction, in their Capitec app.
- The user is redirected back to the merchant platform, and the granted (successul) consent status is confirmed with a webhook notification from Stitch.
- Once a consent is granted, charges may be made to debit the user. Transaction frequency and amounts must be in accordance with the granted consent terms.
Create a Variable Payment Consent Request
A Payment Consent Request can be created using the paymentConsentRequestCreate
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.
The GraphQL API URL https://api.stitch.money/graphql can be used for all consent requests (whether on test or live clients).
Two different types of Variable Payment Consent Requests exist: Once-off, and Recurring. Payment schedule details and possible amounts distinguish these two types.
Base Request Fields
For all types of Variable Consent Requests, the following mutation input fields apply:
Field | Description | Type | Required |
---|---|---|---|
paymentMethods.capitecPay.enabled | This must be set to true to enable the Capitec VRP method for the consent request. | Boolean | Yes |
paymentMethods.capitecPay.paymentDescription | A descriptive field for the consent, displayed to the user on their Capitec app during authorization. | String | Yes |
payerInformation | The user's details as per your system. At a minimum, payerId (a unique payer identifier on your system) and mobileNumber are required within this object. | PayerInformation | Yes |
nonce | A unique value, that may only be used once to ensure the uniqueness of the consent request. | String | Yes |
To create a Payment Consent Request with Capitec Pay, the user will require one of the following payerInformation
fields, when authorizing:
- South African identity number, as registered with Capitec,
- Mobile phone number, as registered with Capitec, or
- Capitec bank account number.
Temporary residence numbers and passports are not currently supported.
Variable Once-off Consent
The mutation to create a variable once-off consent request is shown below:
Specific fields applicable for Once-off Consents (within the capitecPay
method, and in addition to the base fields) are as follows:
Field | Description | Type | Required |
---|---|---|---|
paymentSchedule.frequency | This must be set to ONCE_OFF to create a variable once-off request. This frequency will be displayed to the user on their mobile banking app. | Enum | Yes |
paymentOptions.variable.max | This is the maximum amount for all combined charges on the consent. Displayed to the user on their Capitec app. | Money | Yes |
paymentOptions.tipOptions.amount | This is the tip amount that applies to the consent (included as part of the maximum amount). Displayed to the user on their Capitec app. | Money | Optional |
The tip amount specified on the consent is simply a UI feature, and is presented to the user on their Capitec banking app for approval.
Variable Recurring Consent
An example mutation to create a variable consent request is shown below:
The above mutation specifies a fixed recurring payment. However, a variable payment can be configured by specifying the paymentOptions.variable.max
property instead.
Specific fields applicable for Recurring Consents (within the capitecPay
method, and in addition to the base fields) are as follows:
Field | Description | Type | Required |
---|---|---|---|
paymentSchedule.frequency | This must be set to the expected frequency of transaction charges for the consent, and may be DAILY , WEEKLY , MONTHLY , or YEARLY . This frequency will be displayed to the user on their Capitec app during authorisation. | Enum | Yes |
paymentOptions.fixed.amount | This an amount that would be fixed per future transaction charged on the consent. | Money | Yes, if a variable amount is not specified. |
paymentOptions.variable.max | Defines a maximum amount for a variable recurring amount. Displayed to the user on their mobile banking app. | Money | Yes, if a fixed amount is not specified. |
Handle Interaction URL
The redirect URL returned by the API requires that a redirect_uri
is appended as a query string parameter.
Once a user is redirected to this URL, they will be guided through the process of authorising the consent request.
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 user 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
The URL specified as the redirect_uri
must be secure i.e. an HTTPS URL.
When the user completes or cancels the consent request, they'll be redirected back to the redirect_uri
.
The below query string parameters will be passed back to the redirect_uri
.
Parameter | Description | Type |
---|---|---|
id | The unique consent ID of this payment consent 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 while authorizing. | 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.
Handling Granted Consents
When a user approves the consent, it will move into a PaymentConsentGranted
state. You will a receive a webhook to notify you of this status change.
At this point, you may proceed to initiate transactions using the respective consent request ID as the token.
If 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. 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 user. |
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 user, and can be charged against. |
PaymentConsentCancelled | The consent request has been cancelled 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 Variable Consent Request Payload
{
"clientId": "test-950f1098-dacf-4105-9958-c2319ab1a0bb",
"data": {
"createdAt": "2025-07-03T13:09:45.929+00:00",
"id": "cGF5bWVudGNvbnNlbnRyZXF1ZXN0LzhmZjA2YWE0LWZkMzYtNDFlYi05NTlmLTQ0ZDQ0OTYyYzhlNA==",
"status": "GRANTED",
"type": "CAPITEC_PAY_RECURRING",
"updatedAt": "2025-07-03T13:09:55.955+00:00"
},
"datetime": "2025-07-03T13:09:55.975Z",
"id": "cGF5bWVudGNvbnNlbnRyZXF1ZXN0LzhmZjA2YWE0LWZkMzYtNDFlYi05NTlmLTQ0ZDQ0OTYyYzhlNA==",
"type": "payment-consent-request"
}
Testing and Simulation
When using a test client, the default flow will result in a PaymentConsentGranted
status, 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. | PaymentConsentGranted |
timeout | Simulates the user not responding to in-app prompt before the request expires. The user may be able to retry. | PaymentConsentPending |
declined | Simulates the user declining the in-app prompt on their device. | PaymentConsentPending |
clientTransactionLimitExceeded | Simulates the user not being able to authorise the consent due to transaction limits set in the Capitec app. | PaymentConsentPending |
clientAppNotFound | Simulates the user not being able to authorise the consent due to not having the Capitec app installed. | PaymentConsentPending |
clientDeactivated | Simulates the user not being able to authorise the consent due to a deactivated Capitec account. | PaymentConsentPending |
clientBlockedMerchant | Simulates the user not being able to authorise the consent due to blocking the merchant on the Capitec app. | PaymentConsentPending |
clientNotFound | Simulates the user not entering a valid identifier for a Capitec account. | PaymentConsentPending |