Recurring charges
The Pismo platform's Recurring charges API enables you to set up recurring charge plans for accounts. For example, you could use it to set up an annual fee.
To set up a plan for an account, you first create the plan, then create a link between the plan and the account. Once the two are linked, the plan is automatically activated, and the account can begin incurring charges based on the plan settings. One plan can be linked to multiple accounts, and one account can be linked to multiple plans.
An account can even be linked to the same plan multiple times, as long as each link has a different
tracking_id
.
You can deactivate a plan for a specific account by deleting the link between them, or you can disable the plan altogether.
These processes are explained in more detail in the following sections.
Recurring charge plans
Create a plan
To create a recurring charge plan, use the Create recurring charge plan endpoint. If successful, this endpoint returns an ID for the recurring charge plan, which can be used with the other endpoints in the API.
A recurring charge plan is defined on the organization (Org) level.
Disable a plan
To disable a recurring charge plan, use the Disable recurring charge plan endpoint. Once a plan is disabled, it can no longer be linked to accounts. If you set disable_all_accounts
to true, the endpoint also deletes any existing links associated with the plan, and the accounts associated with the deleted links no longer incur the charges specified in the plan.
If disable_all_accounts
is false (the default), then accounts that were linked to the plan before it was disabled remain linked to it, and the accounts continue to incur charges based on the plan.
Recurring charge links
Create a link
To link a recurring charge plan to an account, use the Create link endpoint. If successful, this endpoint returns an ID for the link.
Delete a link
To deactivate a plan that's linked to an account, use the Delete recurring charge link endpoint. If successful, the link between the plan and the account is deleted, and the account no longer incurs the charges specified in the plan. This does not delete the plan itself, and any links the plan has with other accounts are unaffected.
Recurring scheduled charges
Recurring scheduled charges are recurring charges with status PENDING
. In other words, they are charges that are scheduled, but have not yet been incurred by the account.
View scheduled charges
Use the List scheduled charges endpoint to retrieve a list of scheduled charges filtered by query parameters. The returned list includes the ID for each charge in the list. Use the Get scheduled charge endpoint with this ID to retrieve the information for a specific charge.
Update a scheduled charge
Use the Update scheduled charge endpoint to modify a scheduled charge.
Update scheduled charge does not change the recurring charge plan, which might be linked to multiple accounts. It updates one charge for one cycle/statement of one account.
Examples for recurring charge plans
Example 1
Suppose you define a recurring charge plan with the following settings:
number_of_cycles
= 6installment_amount
= 20first_cycles_to_discount
= 2discount_percentage
= 10split_transaction
= true
Since split_transaction
is true, two transactions are posted to the account for each discounted installment — a debit transaction for the installment amount and a credit transaction for the discount. (If an installment is not discounted, only one transaction is posted.)
You specify the type of transaction used for the installment using
processing_code
. Ifsplit_transaction
is true, you specify the type of transaction used for the discount usingsecondary_processing_code
. So, normally, you would useprocessing_code
to specify a debit transaction andsecondary_processing_code
to specify a credit transaction. However, you aren’t forced to do this. You could make both transactions debits, for example. It’s unlikely that you’ll encounter a scenario where you would need to do something like this, so it's not recommended.
installment_amount
is the charge per cycle before any discount is applied. Since the discount percentage is 10%, the actual discount is $2 (the installment amount $20 multiplied by 0.1). first_cycles_to_discount
is 2, so the charge is $18 ($20 - $2) for the first two cycles and $20 for the next four cycles.
What happens after cycle 6 depends on what you specify for renew_method
. There are three possible settings:
renew_method | Description |
---|---|
NO_RENEW | The link does not renew after number_of_cycles have passed. In the example, no more charges are incurred after cycle 6 as a result of the plan. |
WITH_DISCOUNT | The link is renewed after the specified number of cycles have passed, and the discount is applied again. In the example, the discount is applied to cycles 1 and 2, but no discount is applied to cycles 3 through 6. After the 6th cycle, the link between the plan and the account is deleted, a new link (with a new link ID) is created, and the sequence repeats, with the same discounts applied to cycles 1 and 2. This sequence of events continues to repeat indefinitely. |
WITHOUT_DISCOUNT | The link is renewed after number_of_cycles have passed, but the discount is not applied.In the example, after the 6th cycle, the link between the plan and the account is deleted and a new link is created. In this case, however, the full charge of $20 is incurred for all 6 cycles (assuming the account balance does not drop below minimum_spend_to_charge ). The link continues to be recreated every 6 cycles with the full charge incurred for each cycle. |
Suppose you set renew_method
to WITH_DISCOUNT. You create the plan using a request similar to this:
curl --request POST \
--url https://sandbox.pismolabs.io/recurring-charge/v1/recurring-charge-plans \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'x-cid: 5bb05174-4e80-11ea-b77f-2e728ce88125' \
--data '
{
"split_transaction": true,
"description": "Annuity {counter}",
"secondary_description": "Discount {counter}",
"number_of_cycles": 6,
"first_cycles_to_discount": 2,
"discount_percentage": 10,
"minimum_spend_to_charge": 100,
"renew_method": "WITH_DISCOUNT",
"tracking_id": "3ea22692-a209-11eb-89a2-f73e179dcbc2",
"processing_code": "99066",
"secondary_processing_code": "99067",
"installment_amount": 20
}
Notice that minimum_spend_to_charge
is set to $100. This is the minimum amount of debt (total debits) that the account must have for charges to be incurred. Assume the account balance does not drop below this amount. With these settings, the client incurs the following charges on their statements:
Cycle number | Installment amount | Discount | Charge |
---|---|---|---|
1 | $20 | $2 | $18 |
2 | $20 | $2 | $18 |
3 | $20 | $2 | $20 |
4 | $20 | $2 | $20 |
5 | $20 | $2 | $20 |
6 | $20 | $2 | $20 |
Since split_transaction
is true, two transactions — a debit transaction for the base amount of the installment (the amount before the discount is applied) and a credit transaction for the discount amount — are posted to the account for cycles 1 and 2. So, for example, on the statement for cycle 1, there would be a debit transaction in the amount of $20 and a credit transaction for $2. Both transactions would appear on the statement.
Since renew_method
is set to WITH_DISCOUNT, the same charges repeat in the next 6 cycles and the next 6, and so on.
Example 2
Suppose you define a recurring charge plan with the same settings as in Example 1, but with split_transaction
set to false:
number_of_cycles
= 6installment_amount
= 20first_cycles_to_discount
= 2discount_percentage
= 10split_transaction
= falserenew_method
= WITH_DISCOUNT
You create the plan using a request similar to this:
curl --request POST \
--url https://sandbox.pismolabs.io/recurring-charge/v1/recurring-charge-plans \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header 'x-cid: 5bb05174-4e80-11ea-b77f-2e728ce88125' \
--data '
{
"split_transaction": false,
"description": "Annuity {counter}",
"number_of_cycles": 6,
"first_cycles_to_discount": 2,
"discount_percentage": 10,
"minimum_spend_to_charge": 100,
"renew_method": "WITH_DISCOUNT",
"tracking_id": "3ea22692-a209-11eb-89a2-f73e179dcbc2",
"processing_code": "99066",
"installment_amount": 20
}
The charges incurred are the same as in example 1, however, only one transaction is created for each installment. For cycle 1, for example, there would be one debit transaction for $18 posted to the account. The installment would also appear as one transaction on the statement.
Example 3
Suppose you want to charge an annual fee of $120. There are two ways to do this:
- You could split the charge across the cycles:
number_of_cycles
= 12
installment_amount
= 10 - You could set
installment_amount
to 120 and discount the first 11 cycles by 100%. This would result in no charges for the first 11 cycles and a charge of $120 in cycle 12:
number_of_cycles
= 12
installment_amount
= 120
first_cycles_to_discount
= 11
discount_percentage
= 100
Examples for scheduled charges
Example 1
When you use List scheduled charges or Get scheduled charge to view scheduled charges, you must take into account the value of split_transaction
. If split_transaction
is true, then the installment_amount
field returned by these endpoints has the same value that you entered when you created the plan, and the secondary_installment_amount
field represents the value of the discount (if any) that's applied to installment_amount
. See example 1 in the last section.
If split_transaction
is false, then the discount (if any) is deducted from installment_amount
and saved back into the same field. In other words, installment_amount
is set equal to installment_amount
minus the discount. So, the new installment amount can be different depending on whether the charge is discounted.
The following code shows the kind of results you might get from running List scheduled charges. In this case, only one charge is listed because per_page
is set to 1. There is a value for secondary_installment_amount
, so the plan probably has split_transaction
set to true, although this could have been overridden using Update scheduled charge. (See next example.)
{
"current_page": 1,
"per_page": 1,
"pages": 10,
"total_items": 10,
"is_last_page": true,
"items": [
{
"recurring_scheduled_charge_id": 880077,
"org_id": "TN-cc8f8b89-233a-4582-9f36-63ee85278d6d",
"account_id": 6912345,
"created_at": "2023-03-30T12:26:41",
"updated_at": "2022-02-01",
"statement_id": 9497889,
"recurring_charge_link_id": 98660282,
"status": "PENDING",
"creation_cid": "f7ad1742-cae5-4d59-be97-7d6127b363ea",
"cancellation_cid": "10a30b55-53a0-4cf9-9e20-25a6ccec12dd",
"processing_code": "99066",
"installment_amount": 10.9,
"description": "Annuity 1/12",
"authorization_id": 1099901,
"authorization_tracking_id": "fd0e5a8a-bc33-436a-8fba-0e82c7d7c12e",
"secondary_processing_code": "99067",
"secondary_installment_amount": 5.45,
"secondary_description": "Discount",
"secondary_authorization_id": 1099901,
"secondary_authorization_tracking_id": "7ec3b2b2-8e70-4b0a-ac4d-86c6275310c2"
}
]
}
If you supply a value in the
description
field when you create a link, this value is used in thedescription
field of all the charges, as in the above example. If you don't provide a value fordescription
in the link, thedescription
field of the plan is used.The
secondary_description
field in the charge always gets its value from the plan.
Example 2
When you use Update scheduled charge, it's possible to split the charge between installment_amount
and secondary_installment_amount
, even if split_transaction
is false. However, to do this, you need to enter a value for secondary_processing_code
. Otherwise, you get an error.
Suppose you have a recurring charge plan with the following settings:
number_of_cycles
= 6installment_amount
= 20first_cycles_to_discount
= 2discount_percentage
= 50processing_code
= "99066"split_transaction
= false
The plan is applied to an account with ID 3512347. When you run List scheduled charges on this account, the result includes the following information for one of the scheduled charges:
"items": [
...
{
"recurring_scheduled_charge_id": 450066,
"org_id": "TN-cc8f8b89-233a-4582-9f36-63ee85278d6d",
"account_id": 3512347,
"created_at": "2023-02-12T11:19:11",
"updated_at": "2023-02-20",
"statement_id": 9497889,
"recurring_charge_link_id": 12560211,
"status": "PENDING",
"creation_cid": "22bccfdc-e282-4b33-afa4-56e9dd30783b",
"cancellation_cid": "e55ca816-9b9a-424a-a251-9ff76b042030",
"processing_code": "99066",
"installment_amount": 10,
"description": "Annuity 3/6",
"authorization_id": 2059211,
"authorization_tracking_id": "294f167e-dd0f-4e75-b430-8361d8ba8ebe",
},
...
]
Notice that installment_amount
is $10, which is 50% less than the value of installment_amount
that was used to create the plan. This is because split_transaction
is set to false. The value of installment_amount
returned by List scheduled charges is the discounted amount.
Now, suppose you execute the following request:
curl --request PATCH \
--url https://sandbox.pismolabs.io/recurring-charge/v1/accounts/3512347/scheduled-charges/450066 \
--header 'accept: application/json' \
--header 'authorization: Bearer eyJhbGciOiJIUzUxMiIsImtpZCI6Ijk1MzA0YzRjZTAxN2I3NTg3YTFlMGQzMjVjZmVkOTVkZDY3ZTYzNTUifQ.eyJpc3MiOiJQYXNzcG9ydCIsInN1YiI6IkFVVEgtMjc4ZWRmNzktZjQ2MS00ZDAwLTkxNDEtZmYxZWZhZjNhNTEwIiwiZXhwIjoxNjk0NjI0ODc5LCJpYXQiOjE2OTQ2MjQyNzksImFpZCI6IjEyOTc3NjQyMyIsInVpZCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNDo2NzgxOTM3MTY2OGE2MmRjMWIxMjE4YTQ4NGE4YzQ1NTE2NWU0OTkwIiwiYWNjb3VudC1pZCI6IjEyOTc3NjQyMyIsInRrdmVyIjoiMiIsInRlbmFudF9pZCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNCIsInRlbmFudCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNCIsInRva2VuX3R5cGUiOiJjbGllbnRfY3JlZGVudGlhbHMifQ.pIon_X9Y5Su1kKPNvPTRf4pkUxTbuo-nX7Yp-LCl3FTBjmekWQuxbrr0rv6yW0iN8E5DSV83GuSETXK8Bfj7JA' \
--header 'content-type: application/json' \
--data '
{
"installment_amount": 20,
"processing_code": "99066",
"description": "Annuity 3/6",
"secondary_processing_code": "99067",
"secondary_installment_amount": 10,
"secondary_description": "Discount"
}
This request updates the scheduled charge shown in the previous code block. It changes installment_amount
to 20 and sets secondary_installment_amount
to 10. Since split_transaction
is set to false in the plan, you had to include a value for secondary_processing_code
. Assume that "990066" and "990067" are processing codes for a debit transaction and a credit transaction, respectively. Then, the amount of the charge is 20 - 10 = $10 — the same as it was before you executed the request. You've just split the transaction for this one charge.
Example 3
Update scheduled charge can be used to settle a charge. Suppose a client asks the bank to fully discount a customer's installment for a particular cycle. To skip the installment, you execute the endpoint with installment_amount
set to 0 and use the statement ID to specify the cycle you want to skip.
Say you have the same recurring charge plan that was used in the previous example:
number_of_cycles
= 6installment_amount
= 20first_cycles_to_discount
= 2discount_percentage
= 50processing_code
= "99066"split_transaction
= false
The plan is applied to an account with ID 3512347. You run List scheduled charges to find the ID for the statement that you want to skip, which turns out to be the one shown below (the same one as in the last example):
"items": [
...
{
"recurring_scheduled_charge_id": 450066,
"org_id": "TN-cc8f8b89-233a-4582-9f36-63ee85278d6d",
"account_id": 3512347,
"created_at": "2023-02-12T11:19:11",
"updated_at": "2023-02-20",
"statement_id": 9497889,
"recurring_charge_link_id": 12560211,
"status": "PENDING",
"creation_cid": "22bccfdc-e282-4b33-afa4-56e9dd30783b",
"cancellation_cid": "e55ca816-9b9a-424a-a251-9ff76b042030",
"processing_code": "99066",
"installment_amount": 10,
"description": "Link between an account and a recurring charge plan",
"authorization_id": 2059211,
"authorization_tracking_id": "294f167e-dd0f-4e75-b430-8361d8ba8ebe",
},
...
]
To skip the charge for this statement's cycle, you execute the following request:
curl --request PATCH \
--url https://sandbox.pismolabs.io/recurring-charge/v1/accounts/3512347/scheduled-charges/450066 \
--header 'accept: application/json' \
--header 'authorization: Bearer eyJhbGciOiJIUzUxMiIsImtpZCI6Ijk1MzA0YzRjZTAxN2I3NTg3YTFlMGQzMjVjZmVkOTVkZDY3ZTYzNTUifQ.eyJpc3MiOiJQYXNzcG9ydCIsInN1YiI6IkFVVEgtMjc4ZWRmNzktZjQ2MS00ZDAwLTkxNDEtZmYxZWZhZjNhNTEwIiwiZXhwIjoxNjk0NjI0ODc5LCJpYXQiOjE2OTQ2MjQyNzksImFpZCI6IjEyOTc3NjQyMyIsInVpZCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNDo2NzgxOTM3MTY2OGE2MmRjMWIxMjE4YTQ4NGE4YzQ1NTE2NWU0OTkwIiwiYWNjb3VudC1pZCI6IjEyOTc3NjQyMyIsInRrdmVyIjoiMiIsInRlbmFudF9pZCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNCIsInRlbmFudCI6IlROLTZhZWM2MTNmLTQ0YmEtNDE4NC05NDg4LWJmMDM0M2U3NDczNCIsInRva2VuX3R5cGUiOiJjbGllbnRfY3JlZGVudGlhbHMifQ.pIon_X9Y5Su1kKPNvPTRf4pkUxTbuo-nX7Yp-LCl3FTBjmekWQuxbrr0rv6yW0iN8E5DSV83GuSETXK8Bfj7JA' \
--header 'content-type: application/json' \
--data '
{
"installment_amount": 0,
"processing_code": "99066",
"description": "Installment settled",
}
Updated 10 months ago