Skip to main content

Migration guide from V2 to V3

What is new in V3

Campaign

The Recurring API V3 adds new campaign types. See Campaigns in the API Guide.

Reserve capture

The Recurring API V3 adds the functionality to do reserve and capture on recurring charges. See Reserve capture in the API Guide.

Partial capture

The Recurring API V3 adds the functionality to do partial capture on reserved charges. See partial capture in the API Guide.

What has changed in V3

Response statuses

The API V3 returns different response status for some endpoints:

EndpointV2 response statusV3 response status
PATCH:/agreements/{agreementId}200 OK204 No Content or 202 Accepted*
DELETE:/agreements/{agreementId}/charges/{chargeId}200 OK204 No Content or 202 Accepted*
POST:/agreements/{agreementId}/charges/{chargeId}/capture200 OK204 No Content or 202 Accepted*
POST:/agreements/{agreementId}/charges/{chargeId}/refund200 OK204 No Content or 202 Accepted*

204 No Content indicates that the request was successfully processed.

202 Accepted indicates also that the request was successful but the processing has not been completed yet. The request is processed asynchronously and once it is completed, the agreement or charge will be updated. Polling can be done to check the status. See polling guidelines in the API Guide.

Please note: Responses might include a Retry-After-header that will indicate the earliest time you should retry the request or poll the resource to see if an operation has been performed. This will follow the spec as defined here, and will either be a http-date or a number indicating a delay in seconds. This will mostly apply to 429 responses, but may also appear in certain other circumstances where it would be natural to retry the request at a later point in time.

Error responses

In V3, HTTP responses for errors follow the RFC 7807 standard.

See: Errors.

New price representation

The Recurring API V3 introduces a new JSON representation for agreement price.

There is no change on the charge limits rules. We will look into how we can implement charge limits in a better way, that takes care of the merchants needs.

Fixed amount agreement

To draft an agreement with a fixed amount with the same charge limit as in V2, pricing.type should be set to LEGACY. Please note: pricing.type is an optional field. If not provided in the request, pricing.type will be defaulted to LEGACY.

Truncated example of request body for the POST:/agreements endpoint from V2 and the equivalent in V3:

V2 request body

{
"currency": "NOK",
"price": 100000,
"productName": "MyNews Digital",
"customerPhoneNumber": "45678272",
"...": "..."
}

V3 request body

{
"pricing": {
"type": "LEGACY",
"amount": 100000,
"currency": "NOK"
},
"productName": "MyNews Digital",
"phoneNumber": "45678272",
"...": "..."
}

Variable amount

To draft agreement with a variable amount, pricing.type should be set to VARIABLE.

Truncated example of request body for the POST:/agreements endpoint from V2 and the equivalent in V3:

V2 request body

{
"currency": "NOK",
"variableAmount": {
"suggestedMaxAmount": 3000
},
"productName": "MyNews Digital",
"customerPhoneNumber": "45678272",
"...": "..."
}

V3 request body

{
"pricing": {
"type": "VARIABLE",
"suggestedMaxAmount": 3000,
"currency": "NOK"
},
"productName": "MyNews Digital",
"phoneNumber": "45678272",
"...": "..."
}

New interval representation

The Recurring API V3 introduces a new JSON representation for agreement interval.

Truncated example of request body for the POST:/agreements endpoint from V2 and the equivalent in V3:

V2 request body

{
"interval": "MONTH",
"intervalCount": 1,
"productName": "MyNews Digital",
"phoneNumber": "45678272",
"...": "..."
}

V3 request body

{
"interval": {
"unit": "MONTH",
"count": 1
},
"productName": "MyNews Digital",
"phoneNumber": "45678272",
"...": "..."
}

More details on charges

In the API V3, the response from the GET:/agreements/{agreementId}/charges/{chargeId} endpoint contains the history of the charge and not just the current status. It also contains a summary of the total of amounts captured, refunded and cancelled.

Truncated example of the response from the GET:/agreements/{agreementId}/charges/{chargeId} endpoint:

{
"id": "chr_WCVbcA",
"status": "REFUNDED",
"amount": 1000,
"type": "RECURRING",
"transactionType": "RESERVE_CAPTURE",
"...": "...",
"summary": {
"captured": 1000,
"refunded": 600,
"cancelled": 0
},
"history": [
{
"occurred": "2022-09-14T10:31:15Z",
"event": "CREATE",
"amount": 1000,
"idempotencyKey": "e80bd8c6-3b83-4583-a49c-847021fcd839",
"success": true
},
{
"occurred": "2022-09-16T06:01:00Z",
"event": "RESERVE",
"amount": 1000,
"idempotencyKey": "chr-4assY8f-agr_FJw2Anb-ProcessPayment",
"success": true
},
{
"occurred": "2022-09-18T06:01:00Z",
"event": "CAPTURE",
"amount": 1000,
"idempotencyKey": "096b1415-2c77-4001-9576-531a856bbaf4",
"success": true
},
{
"occurred": "2022-09-20T06:01:00Z",
"event": "REFUND",
"amount": 600,
"idempotencyKey": "0bc7cc3b-fdef-4d24-b4fe-49b7da40d22f",
"success": true
}
]
}

Idempotency key

The misspelled Idempotent-Key header is deprecated. TheIdempotency-Key header is now required for the POST and PATCH endpoints. See Idempotency key header in the API Guide.

Product description guidelines

We do not recommend you to use Product Description for agreements with a campaign. We see that the user experience is not optimal when a lot of text is "squeezed" in the purple bubble displaying an agreement. See Campaigns in v3 in the API Guide.

Update an agreement

In the API V3, it is possible to update the same fields on an agreement as in V2 except campaign. A campaigns can not be updated after creation.

Field in V2Field in V3
productNameproductName
productDescriptionproductDescription
statusstatus
merchantAgreementUrlmerchantAgreementUrl
pricepricing.amount
suggestedMaxAmountpricing.suggestedMaxAmount
campaignCannot be updated in V3

See the PATCH:/agreements/{agreementId} endpoint.

Also, the API V3 returns different response status. It will return 204 No Content or 202 Accepted. See Response statuses