payconiq
v0.3.0
Published
Node.js implementation for interacting with the Payconiq V3 API
Downloads
11
Maintainers
Readme
Payconiq
Node.js implementation for interacting with the Payconiq V3 API
Disclaimer: this is not an official package from Bancontact Payconiq Company or any affiliated entities or subsidiaries. This is my own implementation following the API Docs. In no way do I make any guarantees that this code is valid, safe, or well-written. That being said, I wrote this code for my own use, and if you want to use it in your project(s), you're free to do so in accordance to the license.
Payconiq is a Belgian digital payment provider with different products. This package aims to have a simpler way of using the Payconiq API V3.
Preamble
This package is in no way feature-complete. I needed to use the Payconiq API for a project and decided to make a node package out of it for my own enjoyment. Thus the roadmap and (planned) features are mostly decided by my needs. That being said, I do try to make the use and implementation general enough such that it can be used in any project. I also more than welcome feature requests or pull requests.
Requirements
Node.js 17.5+
because this package aims to have 0 dependencies, thus native fetch is needed.
TypeScript 4.7+
for ESM support, of course use of TypeScript is optional.
Installation
Install using npm:
npm install payconiq
Usage
import { PayconiqInvoice } from "payconiq"
// Every payconiq product has its own class
// Here, the invoice product is used
const payconiqInvoicing = PayconiqInvoice("lessSecretPaymentId", "verySecretAPIKey");
// deeplink
const invoiceURL = payconiqInvoicing.makePayment({
amount: 1234,
description: "products from us",
reference: "myOwnReference"
});
// qr-code
const invoiceQRURL1 = payconiqInvoicing.makeQRcode({amount: 1234, …})
const invoiceQRURL2 = payconiqInvoicing.makeQRcode(invoiceURL, {format: "SVG"})
// verify callback
const isLegit = payconiqInvoicing.verify(signatureFromRequest, bodyFromRequest, {callbackURL: 'https://…'})
Reference
Quick Overview
For every (implemented: instore, predefined, invoice, receipt) URL-string product there is a class exported: PayconiqInstore
, PayconiqPredefined
, PayconiqInvoice
, and PayconiqReceipt
. Since they all need an API key in their constructor it should only be used in the backend.
Common Across Products
All product classes have common or at least similar elements.
Constructor
The constructor parameters are the same for all product classes:
ppid
(string) .:. paymentId given by Payconiq.apiKey
(string) .:. API key given by Payconiq.productOptions
.:. an object with some optional keys:callbackURL
(string | null) .:. your callbackURL used to check the critical headerpath
of requests. Usenull
to explicitly skip checking this critical header.defQRCodeOpts
(object) .:. if you want to set defaults for generated QR-codes.environment
("PROD"
or"EXT"
) .:. for use of the external of production environment, default is"PROD"
.
Methods
Every product has the same basic set of methods. However, their exact use may differ, mostly in being synchronous or asynchronous and its arguments. PayconiqInstore
is the exception to the rule by not having a makeQRcode
method at all.
makePayment(…)
.:. depends on the product where it may have to register the payment against URL-string servers or only generate a URL-string identifying a payment
makeQRcode(…[, QRCodeOpts])
.:. will return a URL-string of the QR-code. The given QRCodeOpts
will overwrite the ones in the constructor, and use the constructor ones when omitted.
Everywhere a Payconiq QR-code is generated, an object to customize it can be passed in. This object can contain any of the following entries, all of them being optional but overwriting the defaults set in the constructor for that function call. Payconiq should default to the the first value for every option: a small magenta png.
format
.:."PNG"
, or"SVG"
size
.:."S"
,"M"
,"L"
,"XL"
color
.:."magenta"
or"black"
verify(signature, body[, verifyOpts])
.:. returns a boolean and is used to verify an incoming request from Payconiq servers to your callbackURL. Eventhough this method is shared among all product classs, the verification checks for ppid
and thus should be used on the corresponding instance. The verifyOpts
parameter is an object with following optional keys:
callbackURL
(string | null) .:. your callbackURL used to check the critical headerpath
of requests. Usenull
to explicitly skip checking this critical header. If not given, the one given to the constructor is used. However, skipping the use in both constructor and method will result in a runtime error unless one of the two is explicitly set tonull
to skip the check altogether.now
(number) .:. the timestamp to be used to compare against for the critical headeriat
, in milliseconds since epochmaxAgeMs
(number) .:. the maximum allowable time difference with the critical headeriat
wheniat
is earlier thannow
. A hard limit of 100ms is set whennow
is earlier thaniat
.
fetchJWKS({force, JWKS, lastFetch})
.:. (re)fetches the JWKS from Payconiq servers. Force (re)fetching by setting force
to true
. If you fetched the JWKS seperately, you can pass it in as JWKS
and set its fetch time with lastFetch
.
JWKS Handling
The JWKS is lazily fetched and cached. So the first verify
call will check if there is a JWKS in the cache. If not or if they are too old or if force
is set to true
, they will be fetched and cached for later used. This can lengthen a (first) verify
call. To expedite a verify
call and alleviate it from having to (re)fetch JWKS, you can fetch them beforehand using the fetchJWKS
method. The JWKS cache is shared among all products of the same environment, thus manually setting JWKS will also overwrite it for all other products using the same environment (although I am not completely sure when you would want different JWKS for products in the same environment).
Product classes
Currently three products are partly implemented. Since Payconiq can't always make up their minds on how to call them, I chose to use PayconiqInstore
, PayconiqPredefined
, PayconiqInvoice
, and PayconiqReceipt
.
Payment amounts are always given in (euro)cents (integer).
Payconiq only allows "EUR"
as currency but relevant methods still include it for completeness.
PayconiqInstore
Also referred to as ECR sticker or just sticker.
General info
This product is just a fixed QR-code pointing to the merchant. The client inputs the payment amount. I have not found a way to make a link to get the QR-code containing the deeplink. So there is no makeQRcode
method. Although I haven't been able to test it, Payconiq devs assured me these payments also trigger a callback.
Methods
makePayment()
.:. Returns the URL-string that can be used as a deeplink.
PayconiqPredefined
Also referred to as Static QR Sticker or Pre-defined sticker.
General info
This product is based on a Point-Of-Sale id (posId), which identifies a cash register or similar, and can handle 1 payment at a time, but many consecutive ones. When the payment is made, it is only valid for 2 minutes.
Methods
A posId
consists of 1 to 36 characters, where the characters can be numbers or letters. Letters can be uppercase (x)or lowercase but cannot be a mix of lowercase and uppercase (ask me how I know).
makePOSURL(posId)
.:. Returns a URL-string of the given posId
.
makeQRcode(posId, qrCodeOpts)
.:. Returns the URL-string of the QR-code for the given posId
.
makePayment(posId, amount, extra)
.:. Given a posId, an amount and other extra info, it will asynchronously create a payment for that posId, returning the response body in object form. extra
is an object with optional properties (callbackUrl
, currency
, description
, reference
, bulkId
, shopId
, shopName
) as described in the payconiq docs.
cancelPayment(bodyOrCancelLink)
experimental.:. Given the response from makePayment
or the cancel link itself, it will cancel the payment and return true, or error when unsuccessful. This method is untested and behaviour will change.
PayconiqReceipt
General info
A receipt is uniquely defined by three things: the amount
, the description
and the reference
. Trying to make a second distinct receipt with those three things the same, will refer to the same receipt for Payconiq. The amount
cannot exceed 999999 (euro)cents. The description
and reference
are (optional) strings with a length of maximum 35 characters. The description
will be displayed to the customer, the reference
can be used for your own internal reference. Eventhough the reference
won't be made visible in the user's UI, it cannot contain sensitive data since it will be included in the URL. An object containing amount
and the optional description
and reference
is herein refered to as receiptInfo
.
Eventhough no methods using the API key are implemented, it is still needed in the constructor. This is for future use and to make sure your product is really activated (no API key, no product).
Methods
assertReceiptInfo(receiptInfo)
.:. Throws assertion errors when the receiptInfo
seems to be invalid.
makePayment(receiptInfo)
.:. Synchronously returns a URL-string for the given receiptInfo
that can be used as a deeplink.
makeQRcode(receiptInfoOrPaymentURL, qrCodeOpts)
.:. Synchronously returns the URL-string of the QR-code for the given receiptInfo
or from a URL-string (deeplink) like the one returned from makePayment
.
PayconiqInvoice
General info
An invoice is uniquely defined by three things: the amount
, the description
and the reference
. Trying to make a second distinct invoice with those three things the same will refer to the same invoice for Payconiq. The amount
cannot exceed 999999 (euro)cents. The description
and reference
are (optional) strings with a length of maximum 35 characters. The description
will be displayed to the customer, the reference
can be used for your own internal reference. Eventhough the reference
won't be made visible in the user's UI, it cannot contain sensitive data since it will be included in the URL. An object containing amount
and the optional description
and reference
is herein refered to as invoiceInfo
.
Eventhough no methods using the API key are implemented, it is still needed in the constructor. This is for future use and to make sure your product is really activated (no API key, no product). I guess if you really only need makePayment
and makeQRcode
methods in a front-end, you could supply it with a bogus API key.
Methods
assertInvoiceInfo(invoiceInfo)
.:. Throws assertion errors when the invoiceInfo
seems to be invalid.
makePayment(invoiceInfo)
.:. Synchronously returns a URL-string for the given invoiceInfo
that can be used as a deeplink.
makeQRcode(invoiceInfoOrPaymentURL, qrCodeOpts)
.:. Synchronously returns the URL-string of the QR-code for the given invoiceInfo
or from a URL-string (deeplink) like the one returned from makePayment
.
Utility and Others
For your convenience an object mapping the product names ("instore"
, "predefined"
, "invoice"
, "receipt"
) to their classes (PayconiqInstore
, PayconiqPredefined
, PayconiqInvoice
, PayconiqReceipt
) is exported. Similarly two generic types are exported: PayconiqProductTypeToClass
and PayconiqProductTypeToInstance
mapping the same product names to their respective class types and instance types.
Every product gets a verifier
that is either of the class PayconiqVerify
or PayconiqVerifyEXT
depending on the environment.
When the verify
gets invalid parameters, it will throw an PayconiqCallbackVerificationError
.
A few request and response body types are exported, their names should be self-explanatory: PayconiqResponseError
, PayconiqCallbackBody
, PayconiqPOSRequestBody
, PayconiqPOSResponseBody
, PayconiqPOSCallbackBody
, and PayconiqJOSEHeader
with subtypes PayconiqDebtor
, PayconiqCreditor
, PayconiqLinks
.
Most parameter types are exported as well: PayconiqEnvironment
, PayconiqQRCodeFormat
, PayconiqQRCodeSize
, PayconiqQRCodeColor
, PayconiqQRCodeOptions
, PayconiqReceiptOrInvoiceInfo
, PayconiqJWK
, PayconiqJWKS
, PayconiqJWKSbyKid
, PayconiqStatusCodes
, and PayconiqProductType
.
Missing Features
This package does not include any features for partner integration or the following products: Terminal & Display, Custom Online, App2App, Top-up. This package cannot do payout reconciliation or help with refund services. The supported products are only partly so, in that they miss features for getting payments, getting a payment list or refunding.