You will not need this API if you are using our payment popup.
If you are building a web payment form, check out these card form helpers.
Create a new customer initiated payment order. A successful payment order returns an authorization to later capture the funds. A payment order by itself does not move funds.
A payment order may be for a future payment such as in a "save card" scenario or a subscription. See the payments plans section for further information.
This endpoint can be used from a server or directly from the customer's device. Tokenization of sensitive details should always happen directly from the customer's device. Tokens can then be offloaded to a server which controls the rest of the flow. The server may need to instruct the device to e.g. load an iframe if required by a challenge.
The payments endpoint is challenge/response based.
POST https://b.paylike.io/payments
{
test: {}, // required for test accounts, omit in production
integration: {
key: String,
},
text: String, // optional
amount: Money, // optional
// one of:
card: {
number: Token,
expiry: {
month: Integer, // 1..12
year: Integer, // 2000..2099
},
code: Token,
},
},
// or
applepay: Token,
// or
mobilepay: {
configurationId: String, // obtained from Paylike during MobilePay signup
logo: URL,
returnUrl: URL, // optional, return to this URL for redirect-based flows
},
// optional
custom,
// optional
plan: [
{
amount: Money,
// one of:
scheduled: Date, // future date
// or
repeat: {
first: Date, // optional, default: now
count: Integer, // optional, default: infinite, 1..
interval: {
unit: String, // one of: "day", "week", "month", "year"
value: Integer, // optional, default: 1, 1..
},
},
},
],
// optional
unplanned: {
merchant: Boolean,
customer: Boolean,
},
hints: Array,
}
// →
// one of:
{
challenges: [
{
name: String,
type: String, // one of: "fetch", "background-iframe", "iframe"
path: String,
},
]
}
// or
{
authorizationId: String
}
This should be the merchant's "public key".
For testing and development use a test mode merchant's "public key" and set
test
to the empty dictionary (test: {}
). Omitting test
for a test mode
merchant or vice versa will trigger a TEST_MODE_MIXED
(error) status code. See
the test
parameter for more details.
This field is optional. If none is provided, the merchant's default is used.
The text is forwarded to be shown on the customer's bank statement or similar. There is no guarantee as to how the issuer of the customer's payment instrument (e.g. their bank) will use or show this text.
The maximum length is 1024 characters. For payment cards (such as Visa, Mastercard, etc.) the length must be at least 2 characters and will be truncated to 22 characters.
This field is optional and of the Money
type.
This is the (positive) amount immediately due for reservation on the customer's payment instrument. It could be the total value of an order at a webshop or the price of an introductory trial period for a subscription.
It should be omitted entirely for subscriptions with no amount due immediately or agreements for later use of the payment instrument for yet unknown amounts (e.g. "save card to account").
If the amount has a higher precision than what the payment instrument supports it will be rounded up to the nearest supported amount (e.g. EUR 1.855 for a Visa card will become EUR 1.86). Consult https://github.com/paylike/currencies for the precision supported.
This field should be a token of the pcn
type as obtained from
the vault service.
This field should be a token of the pcsc
type as obtained from
the vault service.
This optional flag signals an agreement is made to store the payment card details for later use by the customer themselves. For example saving payment card data for future purchases in a webshop or to a wallet. It can only be used for purchases that are initiated by the customer and for which the customer is present to complete additional "challenges".
This flag can be combined with card.store.merchant
.
This optional flag signals an agreement is made to store the payment card details for later use by the merchant. For example saving payment card details for pay-as-you-go/usage-based subscriptions or automatic account top-ups.
For any payments series occuring in fixed intervals (e.g. monthly subscriptions)
or predetermined rates (e.g. installments) use the plan
field instead.
This flag can be combined with card.store.customer
.
This field should be an Apple Pay token obtained from the Apple Pay service.
In order to accept payments through MobilePay (Danish payment card-based payment
method), please contact customer service to obtain a configurationId
.
Both an iframe and redirect-based flow is supported. The specific flow is selected by prioritizing between challenges.
As obtained from customer service. For test mode
00000000000000000000000000000000
can be used.
Fully qualified HTTP(s) URL for a logo in PNG or JPG format of width 250px and height 250px.
The maximum length is 1024 characters.
The URL to which the user should be redirected after confirming or declining a
payment. For apps, this would be your app URL as registered with the OS (e.g.
myapp://page
), otherwise it is a URL for the specific order on your website.
This field is a free-form (JSON) field to attach information related to the payment such as an internal reference number.
This field specifies future payments that is being agreed upon as of this payment order.
Please note that the payment plan is not automatically executed. This field merely represents the agreement made between customer and merchant as of this payment order.
Each component represents an amount (amount
) to be due at a future date either
once (scheduled
) or repeated.
Repeating components begin at repeat.first
(defaulting to "now") and repeat
indefinitely or repeat.count
times at a fixed interval (repeat.interval
).
Subsequent payments occur each repeat.interval.unit * repeat.interval.value
after repeat.first
. repeat.interval.value
defaults to 1
.
Please see the examples below for common use cases.
repeat.count
is optional only for the last componentscheduled
andrepeat.first
must be chronologically later than the previous component
Flag the types of unplanned payments the card will be used for. The supported types are:
customer
(initiated by the customer from your website/application)merchant
(initiated by the merchant or an off-site customer)
This is required for unplanned subsequent payments to ensure compliance and high approval rates.
{
// ...
unplanned: {merchant: true},
}
This field is a list of proofs returned upon completing "challenges" to the payment order. See the challenge-response section for details.
The test
parameter is an "object" used to trigger a variety of real-world
scenarioes. All of its properties are optional:
{
card: {
scheme: String, // 'supported', 'unsupported', 'unknown' (default: 'supported')
code: String, // 'valid', 'invalid' (default: 'valid')
status: String, // 'valid', 'invalid', 'expired', 'disabled', 'lost' (default: 'valid')
limit: Money, // see the "Money" type section (default: infinite)
balance: Money, // see the "Money" type section (default: infinite)
},
fingerprint: String, // 'timeout', 'success' (default: 'success')
tds: {
// 3-D Secure
fingerprint: String, // 'timeout', 'unavailable', 'success' (default: 'success')
challenge: Boolean, // default: true
// for "frictionless" flow use "challenge: false"
status: String,
// 'authenticated', 'rejected', 'unavailable', 'attempted' (default: 'authenticated')
},
}
To represent money with decimals in an unambiguous way pass a dictionary of
{currency, exponent, value}
in which currency
is the
ISO 4217 alpha code and exponent
is
the number of decimal places in the value
integer.
To maximize compatibility favor the exponents in the currencies list.
// € 10.99
{
currency: 'EUR',
exponent: 2,
value: 1099,
}
// $ 33.00
{
currency: 'USD',
exponent: 2,
value: 3300,
}
A string in RFC 3339, section 5.6.
Example: 2021-02-22T15:32:01.909Z
It is the default JSON representation in (at least) JavaScript
((new Date()).toJSON()
) and should generally be supported out-of-the-box for
the native date type when formatted as JSON.
- INTERNAL_ERROR
- VERSION_MISSING
- VERSION_UNSUPPORTED
- BODY_INVALID
- REQUEST_INVALID
- TOKEN_INVALID
- TOKEN_TYPE_UNEXPECTED
- TEST_MODE_MIXED
- PAYMENT_INTEGRATION_KEY_UNKNOWN
- PAYMENT_INTEGRATION_DISABLED
- PAYMENT_CHALLENGE_UNAVAILABLE
- PAYMENT_CARD_NUMBER_INVALID
- PAYMENT_CARD_SCHEME_UNKNOWN
- PAYMENT_CARD_SCHEME_UNSUPPORTED
- PAYMENT_CARD_SECURITY_CODE_INVALID
- PAYMENT_CARD_EXPIRED
- PAYMENT_CARD_DISABLED
- PAYMENT_CARD_LOST
- PAYMENT_AMOUNT_LIMIT
- PAYMENT_INSUFFICIENT_FUNDS
- PAYMENT_RECEIVER_BLOCKED
- PAYMENT_REJECTED_BY_ISSUER
- PAYMENT_REJECTED
- PAYMENT_METHOD_ERROR
- TDSECURE_REQUIRED
- TDSECURE_FAILED
- PAYMENT_APPLEPAY_AMOUNT_MISMATCH
- PAYMENT_APPLEPAY_EXPIRED
The payment API is a so-called challenge/response interface meaning a submitted payment order may be met with a list of challenges intead of a final response. The client must then complete one of these challenges and submit the payment order again including any proofs from the challenge.
An implementor can freely choose whether to manage the flow (and its state) from the customer's device (frontend) or from their server (backend) merely relaying challenges (as necessary) to the frontend. Challenges can be completed from the backend unless otherwise noted for the specific challenge type.
If the response returned for a payment order has a challenges
field, it is a
list of available challenges in prioritized order (best first). The client
should complete one of them, preferably the top one.
A challenge in the list has the format {name, type, path}
. The client should
support each challenge type to obtain their proofs. Every proof obtained should
be added to the hints
field (newest last).
After obtaining a proof, the client should submit the payment order once more
with the updated hints
field to receive further challenges or a final
response.
Fetch a path from the service.
The client should swap /payments
for path
and repeat its request (same
request method and body).
The response format is:
{
hints: Array,
}
Concatenate the existing hints
with the list from the response (new hints
last).
Load an HTML iframe on the customer's device.
The client should swap /payments
for path
and repeat its request (same
request method and body).
The response format is:
{
action: String,
method: String, // the "method" to use
fields: Object, // relevant only for method POST
width: Number, // size in pixels
height: Number, // size in pixels
timeout: Number, // optional, milliseconds
hints: Array, // optional
}
If hints
is present, concatenate with the existing hints
(new hints last)
for later requests.
The client should create an HTML iframe element of and submit an HTML "form" to
it for POST
or load a URL for GET
method. The form should have the "action"
attribute of action
and the fields ("input" elements) with "name" and "value"
pairs matching the keys and values from fields
.
width
and height
is the recommended size of the iframe in pixels. They are
not present for background-iframe
. It is not advised to prevent scrolling the
iframe.
Once the iframe concludes it emits a "message" event of the format
{data: {hints: Array}}
. The client should set up a listener for this event on
the window
property. The hints
value should be concatenated with the
existing hints
(new hints last).
The iframe should be removed after concluding or timeout
milliseconds, and
/payment
requested again.
Fetch a path from the service.
The client should swap /payments
for path
and repeat its request (same
request method and body).
The response format is:
{
url: String,
hints: Array,
}
Concatenate the existing hints
with the list from the response (new hints
last).
Redirect the user to the URL in url
(e.g. location.href = url
in
JavaScript).
Fetch a path from the service and pause for an interval.
The client should swap /payments
for path
and repeat its request (same
request method and body).
The response format is:
{
notBefore: Date,
interval: Integer,
hints: Array,
}
Concatenate the existing hints
with the list from the response (new hints
last).
If hints
is empty, do not continue until notBefore
or after interval
milliseconds have passed.
See a stub example client in example-client.js.
A proof-of-concept can be built along the lines of:
mkdir poc && cd poc
curl https://raw.githubusercontent.com/paylike/api-reference/payments/example-client.js > example.js
echo "{}" > package.json
npm install @paylike/client
echo '<script src="./dist.js"></script>' > index.html
npx browserify example.js > dist.js
open index.html
A payment plan represents an agreement between customer and merchant about
future payments such as for usage, a subscription, or installments. They are
defined using the plan
field.
Please note that the payment plan is not automatically executed. This field merely represents the agreement made between customer and merchant as of this payment order.
Be aware that there is no guarantee for a future payment to succeed despite the payment plan. Read our section on recurring payments and plan accordingly.
Future payments may later be created from your server using
our API by referencing the authorizationId
as the transactionId
.
const plan = [
{
amount: {currency: 'EUR', value: 900, exponent: 2},
repeat: {
interval: {unit: 'month'},
},
},
]
The starting date, and first payment, is assumed to be "now" and thus the next
payment is exactly one month from now. The amount
for the payment should be €9
to immediately authorize the first amount.
const plan = [
{
amount: {currency: 'EUR', value: 7900, exponent: 2},
repeat: {
first: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),
interval: {unit: 'month'},
},
},
]
The payment's amount
would simply be omitted unless the trial has an upfront
price.
const plan = [
{
amount: {currency: 'EUR', value: 900, exponent: 2},
repeat: {
first: new Date('2021-01-22'), // next upcoming Friday
interval: {
unit: 'week',
value: 2,
},
},
},
]
The payment's amount
can be used to charge a prorated amount for the time
between "now" and the first subscription payment if desirable.
const plan = [
{
amount: {currency: 'EUR', value: 999, exponent: 2},
repeat: {
interval: {unit: 'month'},
count: 3,
},
},
{
amount: {currency: 'EUR', value: 1999, exponent: 2},
repeat: {
interval: {unit: 'month'},
},
},
]
const plan = [
{
amount: {currency: 'EUR', value: 400, exponent: 2},
scheduled: new Date('2022-02-01'),
},
{
amount: {currency: 'EUR', value: 400, exponent: 2},
scheduled: new Date('2022-03-01'),
},
{
amount: {currency: 'EUR', value: 300, exponent: 2},
scheduled: new Date('2022-04-01'),
},
]