Moving from v1 to v2
v2 launches in early 2027, and v1 is supported for years after it, so move when it suits you. The objects stay what they were: payment links, transactions, subscribers, seq, and pings. What changes is how you name them, how strictly we read a request, and how clearly an error says what went wrong.
Until v2 launches, its pages are its spec, and may change.
Requests
v2 names the shop in every URL, and an object by its kind and ID, where v1 takes the shop from the key:
| v1 | v2 |
|---|---|
POST /v1/new |
POST /v2/{shopid}:pay/new |
POST /v1/new with "subscriber" |
POST /v2/{shopid}:sub/new |
POST /v1/transactions/{trnid}/capture |
POST /v2/{shopid}:trn:{trnid}/capture |
POST /v1/transactions/{trnid}/refund |
POST /v2/{shopid}:trn:{trnid}/refund |
| None: a void is made in the dashboard | POST /v2/{shopid}:trn:{trnid}/void |
POST /v1/subscribers/{subid}/charge |
POST /v2/{shopid}:sub:{subid}/charge |
POST /v1/subscribers/{subid}/renew |
POST /v2/{shopid}:sub:{subid}/renew |
| None | DELETE /v2/{shopid}:pay:{payid} |
| None | DELETE /v2/{shopid}:sub:{subid} |
GET /v1/seq/{seq} |
GET /v2/{shopid}:seq:{seq} |
See v2's URLs.
Keys
A v1 key is {shopid}:{secret}. A v2 key starts with whose it is, and
the key's own number: s{shopid}#{keyid}:{secret} for a shop's key,
and there are keys for users and partners too. Each v2 key has its
own permissions and its own ping URL, where a v1 shop has one ping URL
for all its keys. See v2's Authentication.
Stricter requests
v1 reads a request leniently, and makes the best of what it can't use. v2 refuses it, so that a mistake shows at once and not in the payments:
- A field of the wrong type, or
null, is ignored in v1, and an error in v2: see v2's Request bodies. "orderid"is optional in v1, and required in v2.- An amount with more decimals than the currency has is cut down in
v1, as
"10.999 DKK"to 10.99 DKK, and an error in v2: see v2's Amounts. - A
"lifetime"that is too short, too long, or unreadable becomes the nearest one v1 allows, or an hour, and is an error in v2, which takes"5m"to"30d". "index"may be left out of a capture or a refund in v1, which reads it as 0, and is required in v2.- v1 accepts a payment that the bank authorizes only in part, for
what it authorized; v2 declines it, so a transaction is always for
the whole amount. In v1, check
totals.authorized, as What was authorized says.
Answers
- Create payment link answers with the link's
"id"beside its"url"in v2, so that you can delete the link, and match it to its transaction. - v1 answers most failures with
500, and its message says what failed. v2 answers each kind with its own status:404for an object that doesn't exist,409for a conflict, such as409 index mismatch,402for a declined charge, and410for a deleted subscriber. See v2's Errors. - A declined charge in v2 says whether to try again: its message
starts with
transientorpermanent. See When a charge fails. Idempotency-Status: OKis on every answer to a request with a key in v1, and marks only a replay in v2.- In seq, an act says who made it with
"who", a key's or a user's ID, in v2, where v1 has"uid"and"apikey". A transaction's card is always undermethod.cardin v2, where v1 names the object for the method, asmethod.applepay. See v2's transaction object.
How to move
- Get v2 keys, and give each a ping URL.
- Change your requests' URLs, and send
"orderid"and"index"always, and fields of the types that v2's reference gives. - Branch on v2's status codes where you parsed v1's messages, and on
transientandpermanentfor a failed charge. - Read seq's acts by
"who", and the card frommethod.card. - Test it all in the test environment, as for a first integration: Before going live.