Clients:
- Node.js: https://github.com/paylike/node-api (official)
- .NET: https://github.com/paylike/Paylike.NET (maintained by @andronachev)
- .NET Standard 2.0 (.NET Core) https://github.com/mrlund/Paylike.NETstandard (maintained by @mrlund)
- PHP: https://github.com/paylike/php-api (maintained by @ionutcalara)
- Python: https://pypi.org/project/paylike/ (maintained by @SuneKjaergaard)
- Java: https://github.com/paylike/java-api (maintained by @jankjr)
- Go: https://github.com/paylike/go-api (maintained by @Roverr)
Building a client in [insert your favourite language here]? Reach out ([email protected]), we might consider sponsoring the maintenance.
Make sure to subscribe to our mailing list for deprecation notices, API changes and new features, or you can watch this repository for changes.
See also:
Backwards incompatible changes including migration paths will be announced on the mailing list and here 6 months in advance providing you plenty of time to update.
- Basics
- Apps
- Merchants
- Lines
- Transactions
Cards- Disputes
- Fraud alerts
- Recurring payments
- Generate payment link
An API key (secret) can be obtained by creating a merchant and adding an app. If your app's target audience is third parties, please reach out and we will make your app's API key hidden.
The service is located at https://api.paylike.io
.
All requests are performed with a basic authorization header containing the API key as password:
curl <url> \
-u :<api-key>
Request body data can be send with either application/x-www-form-urlencoded
(standard form data) or application/json
as Content-Type
header and data
formatted accordingly:
curl <url> \
-u :<api-key> \
-d key="val" \
-d key2="val2"
When using application/x-www-form-urlencoded
nested properties like
company.country
of a merchant can be provided using form data as
company[country]
and booleans should be either "yes" or "no" (e.g. curl [..] -d test=no
).
All successful calls will return a 2xx status code. 4xx is used for errors in your end (validation, constraints, etc.) and 5xx is for server errors (please report those). Take a look at how we use status codes.
Add header Accept: application/json
(curl: -H 'Accept: application/json'
)
for forward compatibility although the service will return JSON at the moment.
Pagination is achieved using after
, before
and limit
parameters (limit
is required where pagination is supported).
The idea is that you start with limit
only, then put the last primary key
(id
) as before
in the next request. This way you can have a reliable
experience even when dealing with lists that are increasing in realtime (such
as transactions).
All lists are by default sorted with the newest entry first.
Using after
will automatically reverse the sort order. Use it for retrieving
newer items.
All amounts are represented in minor (e.g. "DKK 9.95" is represented as 995).
A machine with an API key as credentials.
curl -i https://api.paylike.io/apps \
-d <data>
Expected input data:
{
name: String, // optional
}
Supply a name if you are developing a third party app. It will then be shown instead of the app's key (which you should always keep secret).
Will return:
{
app: {
id: String, // unique key for referencing
name: String,
key: String, // secret key used for authentication
}
}
Get information about the authenticated app, such as the id
and name
.
curl -i https://api.paylike.io/me \
-u :<api-key>
Will return:
{
identity: {
id: String, // unique ID for referencing
name: String, // name of your app, if it has one
created: String, // creation date of your app
}
}
Has a funding bank account and contains all transactions.
Can have several users and apps associated. All users and apps have complete access to the merchant and to invite and revoke others.
Make sure to mark accounts as test when implementing.
curl -i https://api.paylike.io/merchants \
-u :<api-key> \
-d <data>
Expected input data:
{
name: String, // optional
currency: String, // required, three letter ISO
test: Boolean, // optional, defaults to false
email: String, // required, contact email
website: String, // required, website with implementation
descriptor: String, // required, text on client bank statements
company: {
country: String, // required, ISO 3166 code (e.g. DK)
number: String, // optional, registration number ("CVR" in Denmark)
},
bank: { // optional
iban: String, // optional, (format: XX00000000, XX is country code, length varies)
},
}
See https://github.com/paylike/countries for a list of supported countries, their code and official currency.
This is the funding currency. Although you can charge customers in any
currency, all transactions will be exchanged to the funding currency upon
capture. A list of all supported currencies is available at
https://github.com/paylike/currencies, notice that only a subset is supported
for funding (marked by funding: true
).
This is the default text that customers will see in their bank upon a charge, if not overwritten when charging or capturing.
See https://github.com/paylike/descriptor for format and restrictions.
Will return:
{
merchant: {
id: String, // unique ID for referencing
key: String, // public key used for transactions and links
..., // more..
}
}
You probably want to store one or both of id
and key
.
The created merchant is automatically associated with the creating entity (user or app).
curl -i https://api.paylike.io/merchants/<merchant-id> \
-X PUT \
-u :<api-key> \
-d <data>
Expected input data (all optional):
{
name: String,
email: String,
descriptor: String,
}
All other data on the merchant is immutable. Create a new merchant or contact us if you need it changed.
curl -i https://api.paylike.io/identities/<app-id>/merchants?limit=<num> \
-u :<api-key>
Query parameters: pagination (required)
curl -i https://api.paylike.io/merchants/<merchant-id> \
-u :<api-key>
The user will receive an email if they are not signed up at Paylike, or if they are not a member of the merchant.
curl -i https://api.paylike.io/merchants/<merchant-id>/users \
-u :<api-key> \
-d <data>
Expected data:
{
email: String, // required
}
curl -i https://api.paylike.io/merchants/<merchant-id>/users/<user-id> \
-X DELETE \
-u :<api-key>
curl -i https://api.paylike.io/merchants/<merchant-id>/users?limit=<num> \
-u :<api-key>
Query parameters: pagination (required)
curl -i https://api.paylike.io/merchants/<merchant-id>/apps \
-u :<api-key> \
-d <data>
Expected data:
{
appId: String, // required
}
curl -i https://api.paylike.io/merchants/<merchant-id>/apps/<app-id> \
-X DELETE \
-u :<api-key>
curl -i https://api.paylike.io/merchants/<merchant-id>/apps?limit=<num> \
-u :<api-key>
Query parameters: pagination (required)
This is the history that makes up a merchant's balance. Captures, refunds, payouts and other fincancial transactions are all represented by a line.
curl -i https://api.paylike.io/merchants/<merchant-id>/lines?limit=<num> \
-u :<api-key>
Query parameters: pagination (required)
An entry on the financial balance of a merchant account.
All lines have a balance
property detailing the final impact on the
financial balance in minor units of the merchant account's currency.
curl https://api.paylike.io/lines/<line-id> \
-u :<api-key>
Will return:
{
id: String,
merchantId: String, // ID of the owning merchant account
test: Boolean, // whether on a test merchant account
created: Date, // Date of transaction
balance: Number, // value in minor units (final impact on account balance)
fee: Number, // fee, if any, in minor units
description: String // optional reference text
}
Additional fields may be available on a line such as a transactionId
for
captures, refunds, etc..
An authorization (reservation) of a given amount and subsequent captures, refunds and voids.
All transactions have a trail
property which is a list of actions. You can
check the type of the action by looking at a property of the same name (e.g.
transaction.trails[0].capture === true
). Each entry also have an amount
property.
Please see our Web SDK or payment links.
Please see the "payments" section of the API reference.
The previous transaction should be the original transaction created by the customer during sign-up.
Make sure to read about recurring payments.
curl -i https://api.paylike.io/merchants/<merchant-id>/transactions \
-u :<api-key> \
-d <data>
Expected input data:
{
transactionId: String, // required, may also be called "authorizationId"
descriptor: String, // optional, will fallback to merchant descriptor
currency: String, // required, three letter ISO
amount: Number, // required, amount in minor units
custom: Object, // optional, any custom data
}
Will return:
{
transaction: {
id: String, // unique ID for referencing
}
}
Deprecated: use the original transaction created by the customer during sign-up instead, as described above.
Make sure to read about recurring payments.
Using a previous transaction is, in most cases, superior to saving a card due to the extra work involved.
You will first need to obtain a card ID.
curl -i https://api.paylike.io/merchants/<merchant-id>/transactions \
-u :<api-key> \
-d <data>
Expected input data:
{
cardId: String, // required
descriptor: String, // optional, will fallback to merchant descriptor
currency: String, // required, three letter ISO
amount: Number, // required, amount in minor units
custom: Object, // optional, any custom reference or data
}
Will return:
{
transaction: {
id: String, // unique ID for referencing
}
}
In case of a processing error, like insufficient funds, you will see a status
code 400
and the response body will contain one of
the processing errors.
The total amount of captures is always less than or equal to the transaction's amount.
curl -i https://api.paylike.io/transactions/<transaction-id>/captures \
-u :<api-key> \
-d <data>
Expected input data:
{
amount: Number, // required, amount in minor units (100 = DKK 1,00)
currency: String, // optional, expected currency (for additional verification)
descriptor: String, // optional, text on client bank statement
}
The only acceptable value for currency
is that of the transaction itself.
The attribute is provied to make sure a user did not tamper with the currency
during authorization. This way, if the capture succeeds, you are guaranteed to
have at least the right amount of money.
The total amount of refunds is always less than or equal to the total amount captured.
curl -i https://api.paylike.io/transactions/<transaction-id>/refunds \
-u :<api-key> \
-d <data>
Expected input data:
{
amount: Number, // required, amount in minor units (100 = DKK 1,00)
descriptor: String, // optional, text on client bank statement
}
A complete or partial cancellation of the reserved amount.
curl -i https://api.paylike.io/transactions/<transaction-id>/voids \
-u :<api-key> \
-d <data>
Expected input data:
{
amount: Number, // required, amount in minor units (100 = DKK 1,00)
}
curl -i https://api.paylike.io/merchants/<merchant-id>/transactions?limit=<num> \
-u :<api-key>
Query parameters: pagination (required)
curl https://api.paylike.io/transactions/<transaction-id> \
-u :<api-key>
Will return:
{
transaction: {
id: String,
merchantId: String, // ID of the owning merchant account
test: Boolean, // whether on a test merchant account
created: Date, // Date of transaction
currency: String, // currency ISO code
amount: Number, // amount in minor units
descriptor: String, // text on bank statement
pendingAmount: Number, // amount available for capture or void
capturedAmount: Number, // amount captured (available for refund)
refundedAmount: Number, // amount refunded (no further action possible)
voidedAmount: Number, // amount voided (no further action possible)
disputedAmount: Number, // amount involed in disputes such as chargebacks
card: {
bin: String, // first 6 numbers in PAN (card number)
last4: String,
expiry: Date,
scheme: String, // "visa" or "mastercard"
},
custom: Object, // custom data
tds: String, // one of "attempt" or "full" if 3-D Secure was applied
recurring: Boolean, // whether the transaction was made from the server
successful: Boolean,
error: false|Object, // contains a processing error if unsuccessful
// list of all captures, voids, refunds and disputes
trail: [
{
// only one of the following will be present
capture: Boolean,
refund: Boolean,
void: Boolean,
dispute: {
id: String,
// only one of the following will be present
won: Boolean,
lost: Boolean,
},
created: Date,
amount: Number, // amount in minor units and transaction currency
/*
Amount in the merchant account's currency in minor units that
the merchant account balance was affected with which in
practice means the actual profit from the transaction after
fees and/or currency conversion.
*/
balance: Number,
fee: Object, // detailed description of fees applied
descriptor: String, // text on bank statement
lineId: String, // ID of the related accounting line
},
],
}
}
Deprecated: use previous transaction IDs to create subsequent transactions as described in the section on recurring payments.
The instructions below are for saving a card from an earlier transaction.
curl -i /merchants/<merchant-id>/cards \
-u :<api-key> \
-d <data>
Expected input data:
{
transactionId: String, // required
notes: String, // optional
}
Will return:
{
card: {
id: String, // unique ID for referencing
}
}
Once you have a card ID, you will be able to create new transactions.
curl https://api.paylike.io/cards/<card-id> \
-u :<api-key>
Will return:
{
card: {
id: String,
merchantId: String,
created: Date,
bin: String, // first 6 numbers in PAN (card number)
last4: String,
expiry: Date,
scheme: String, // "visa" or "mastercard"
}
}
curl -i https://api.paylike.io/disputes \
-u :<api-key>
Query parameters:
- pagination (required)
filter[merchantId]
(required)filter[transactionId]
(optional)
curl https://api.paylike.io/disputes/<dispute-id> \
-u :<api-key>
Will return:
{
dispute: {
id: String,
merchantId: String, // ID of the owning merchant account
transactionId: String, // Related transaction
created: Date,
amount: Number,
reason: {
code: Number,
title: String,
},
due: Date,
}
}
curl -i https://api.paylike.io/frauds \
-u :<api-key>
Query parameters:
- pagination (required)
filter[merchantId]
(required)filter[transactionId]
(optional)
curl https://api.paylike.io/frauds/<fraud-id> \
-u :<api-key>
Will return:
{
fraud: {
id: String,
merchantId: String,
transactionId: String,
created: Date,
reported: Date|undefined,
reason: Number|undefined,
}
}
Reason can be any of:
Reason | Code |
---|---|
Misc | 0 |
Fraudulent usage | 1 |
Fraudulent application | 2 |
Counterfeit | 3 |
Account takeover | 4 |
Card not delivered | 5 |
Card stolen | 6 |
Card lost | 7 |
Cardholder manipulated | 8 |
Things you should be aware of:
- cards expire
- a card could be reported stolen between payments
- the card may not have sufficient funds
- banks might temporarily decline cards
- recurring payments may require the user to manually enable it at their bank
Your flow should gracely handle failures and allow users to pay with another card and as a regular transaction.
Due to some banks not accepting recurring payments (CVC-less) by default a transaction is more likely to be successful with a regular payment thus you should favor regular transactions and use our "new transaction from existing" as an optimization - don't save card details upfront if you can avoid it, that's also good conversion karma.
Present the user with the initial payment (regular transaction with CVC) and ask your user whether they want to subscribe to future payments. On the next payment try creating a transaction - if it fails, ask the user to do the payment manually (with CVC) and restart the process.
An example flow could look like this:
-
(client) The user accepts to have their card saved for future payments either explicitly or by signing up for a subscription
-
(client) A payment popup is shown or a payment link is generated
-
(server) Save transaction ID
-
(server/async) Capture the transaction
This step should be completed only when your services or your goods are dispatched to the customer (immediately for most subscriptions).
-
(server/async, e.g. one month later) Create recurring payment and capture payment
Create a transaction based on the previous transaction ID saved in (3) and capture it, if it fails for whatever reason (expired, not supported, insufficient funds, etc.), notify the customer by email or other means and restart the process from (1)
You do not need to keep track of card expiration, or special-case any rejections if you follow this flow - cards will fail for whatever reason and be replaced by the customer.
You could enhance the flow by creating recurring payments a bit earlier to warn the user if an upcoming payment will fail and needs to be completed manually. Delay the capture for the actual renewal date.
For mPOS and payment links the format of the link should conform to:
https://pos.paylike.io/
?key=<public key>
&test=1
¤cy=<three letter ISO>
&value=<amount in major e.g. 10.99>
&reference=<text shown in dashboard>
&text=<text shown on payment page>
&redirect=<url>
&locale=<locale (e.g. "en_US" or just "en")>
&recurring=y
All except key
are optional. If value
is included the user is shown the
payment page, if not, it is considered an mPOS case and a pre-screen is shown
for manually setting the amount.
test
must be set for test accounts, and omitted in production.
recurring
is used to signal subscriptions or other "save card".
Most European languages are supported for the locale, please open an issue to request others.
Payment link example: https://pos.paylike.io/?key=ba21f7ac-095c-4941-3196-f6ba24effbaf&test=1¤cy=EUR&value=1.95&reference=order%20232&redirect=https://google.com
mPOS link example: https://pos.paylike.io/?key=ba21f7ac-095c-4941-3196-f6ba24effbaf&test=1¤cy=EUR&reference=order%20232