Skip to content

Latest commit

 

History

History
633 lines (483 loc) · 18.5 KB

index.md

File metadata and controls

633 lines (483 loc) · 18.5 KB

Payments

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
}

Parameters

integration.key

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.

text

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.

amount

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.

card.number

This field should be a token of the pcn type as obtained from the vault service.

card.code

This field should be a token of the pcsc type as obtained from the vault service.

card.store.customer

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.

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.

applepay

This field should be an Apple Pay token obtained from the Apple Pay service.

mobilepay

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.

mobilepay.configurationId

As obtained from customer service. For test mode 00000000000000000000000000000000 can be used.

mobilepay.logo

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.

mobilepay.returnUrl

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.

custom

This field is a free-form (JSON) field to attach information related to the payment such as an internal reference number.

plan

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.

Limitations

  • repeat.count is optional only for the last component
  • scheduled and repeat.first must be chronologically later than the previous component

unplanned

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.

Example

{
  // ...
  unplanned: {merchant: true},
}

hints

This field is a list of proofs returned upon completing "challenges" to the payment order. See the challenge-response section for details.

test

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')
  },
}

Types

Money

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.

Examples

// € 10.99
{
  currency: 'EUR',
  exponent: 2,
  value: 1099,
}

// $ 33.00
{
  currency: 'USD',
  exponent: 2,
  value: 3300,
}

Date

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.

Status codes

Challenge/response

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.

Challenge types

fetch

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).

iframe and background-iframe

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.

redirect

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).

poll

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.

Example client (JavaScript)

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

Payment plans

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.

Examples

Monthly subscription of €9

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.

Monthly subscription of €79 with a 14 days trial

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.

Biweekly (every other week) subscription of €9 charged on Fridays

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.

Three months at a reduced rate

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'},
    },
  },
]

Pay a €1100 debt at €400 monthly (installment)

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'),
  },
]