velirapay 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +269 -2
  4. package/dist/cjs/client.d.ts +94 -0
  5. package/dist/cjs/client.js +150 -0
  6. package/dist/cjs/enums.d.ts +156 -0
  7. package/dist/cjs/enums.js +141 -0
  8. package/dist/cjs/errors.d.ts +61 -0
  9. package/dist/cjs/errors.js +120 -0
  10. package/dist/cjs/http/api-response.d.ts +22 -0
  11. package/dist/cjs/http/api-response.js +67 -0
  12. package/dist/cjs/http/transport.d.ts +23 -0
  13. package/dist/cjs/http/transport.js +174 -0
  14. package/dist/cjs/index.d.ts +41 -0
  15. package/dist/cjs/index.js +118 -0
  16. package/dist/cjs/package.json +3 -0
  17. package/dist/cjs/page.d.ts +33 -0
  18. package/dist/cjs/page.js +65 -0
  19. package/dist/cjs/params.d.ts +264 -0
  20. package/dist/cjs/params.js +2 -0
  21. package/dist/cjs/resources/account.d.ts +123 -0
  22. package/dist/cjs/resources/account.js +179 -0
  23. package/dist/cjs/resources/activity.d.ts +23 -0
  24. package/dist/cjs/resources/activity.js +38 -0
  25. package/dist/cjs/resources/api-key.d.ts +27 -0
  26. package/dist/cjs/resources/api-key.js +45 -0
  27. package/dist/cjs/resources/api-resource.d.ts +45 -0
  28. package/dist/cjs/resources/api-resource.js +136 -0
  29. package/dist/cjs/resources/charge.d.ts +154 -0
  30. package/dist/cjs/resources/charge.js +222 -0
  31. package/dist/cjs/resources/coupon.d.ts +35 -0
  32. package/dist/cjs/resources/coupon.js +57 -0
  33. package/dist/cjs/resources/event.d.ts +22 -0
  34. package/dist/cjs/resources/event.js +35 -0
  35. package/dist/cjs/resources/invoice.d.ts +68 -0
  36. package/dist/cjs/resources/invoice.js +104 -0
  37. package/dist/cjs/resources/member.d.ts +33 -0
  38. package/dist/cjs/resources/member.js +54 -0
  39. package/dist/cjs/resources/overview.d.ts +25 -0
  40. package/dist/cjs/resources/overview.js +41 -0
  41. package/dist/cjs/resources/payment-link.d.ts +65 -0
  42. package/dist/cjs/resources/payment-link.js +96 -0
  43. package/dist/cjs/resources/payout.d.ts +92 -0
  44. package/dist/cjs/resources/payout.js +145 -0
  45. package/dist/cjs/resources/pro-payment.d.ts +21 -0
  46. package/dist/cjs/resources/pro-payment.js +35 -0
  47. package/dist/cjs/resources/supported-asset.d.ts +36 -0
  48. package/dist/cjs/resources/supported-asset.js +58 -0
  49. package/dist/cjs/resources/webhook-endpoint.d.ts +93 -0
  50. package/dist/cjs/resources/webhook-endpoint.js +157 -0
  51. package/dist/cjs/services/account.d.ts +12 -0
  52. package/dist/cjs/services/account.js +25 -0
  53. package/dist/cjs/services/activity.d.ts +11 -0
  54. package/dist/cjs/services/activity.js +17 -0
  55. package/dist/cjs/services/api-keys.d.ts +17 -0
  56. package/dist/cjs/services/api-keys.js +29 -0
  57. package/dist/cjs/services/assets.d.ts +9 -0
  58. package/dist/cjs/services/assets.js +13 -0
  59. package/dist/cjs/services/billing.d.ts +8 -0
  60. package/dist/cjs/services/billing.js +14 -0
  61. package/dist/cjs/services/charges.d.ts +25 -0
  62. package/dist/cjs/services/charges.js +49 -0
  63. package/dist/cjs/services/coupons.d.ts +19 -0
  64. package/dist/cjs/services/coupons.js +33 -0
  65. package/dist/cjs/services/events.d.ts +13 -0
  66. package/dist/cjs/services/events.js +21 -0
  67. package/dist/cjs/services/invoices.d.ts +19 -0
  68. package/dist/cjs/services/invoices.js +33 -0
  69. package/dist/cjs/services/members.d.ts +26 -0
  70. package/dist/cjs/services/members.js +45 -0
  71. package/dist/cjs/services/overview.d.ts +7 -0
  72. package/dist/cjs/services/overview.js +13 -0
  73. package/dist/cjs/services/payment-links.d.ts +21 -0
  74. package/dist/cjs/services/payment-links.js +37 -0
  75. package/dist/cjs/services/payout-addresses.d.ts +12 -0
  76. package/dist/cjs/services/payout-addresses.js +21 -0
  77. package/dist/cjs/services/payouts.d.ts +15 -0
  78. package/dist/cjs/services/payouts.js +25 -0
  79. package/dist/cjs/services/service.d.ts +24 -0
  80. package/dist/cjs/services/service.js +83 -0
  81. package/dist/cjs/services/webhook-endpoints.d.ts +31 -0
  82. package/dist/cjs/services/webhook-endpoints.js +61 -0
  83. package/dist/cjs/version.d.ts +2 -0
  84. package/dist/cjs/version.js +5 -0
  85. package/dist/cjs/webhooks/webhook-event.d.ts +43 -0
  86. package/dist/cjs/webhooks/webhook-event.js +88 -0
  87. package/dist/cjs/webhooks/webhook.d.ts +36 -0
  88. package/dist/cjs/webhooks/webhook.js +98 -0
  89. package/dist/esm/client.d.ts +94 -0
  90. package/dist/esm/client.js +146 -0
  91. package/dist/esm/enums.d.ts +156 -0
  92. package/dist/esm/enums.js +138 -0
  93. package/dist/esm/errors.d.ts +61 -0
  94. package/dist/esm/errors.js +103 -0
  95. package/dist/esm/http/api-response.d.ts +22 -0
  96. package/dist/esm/http/api-response.js +63 -0
  97. package/dist/esm/http/transport.d.ts +23 -0
  98. package/dist/esm/http/transport.js +170 -0
  99. package/dist/esm/index.d.ts +41 -0
  100. package/dist/esm/index.js +39 -0
  101. package/dist/esm/page.d.ts +33 -0
  102. package/dist/esm/page.js +61 -0
  103. package/dist/esm/params.d.ts +264 -0
  104. package/dist/esm/params.js +1 -0
  105. package/dist/esm/resources/account.d.ts +123 -0
  106. package/dist/esm/resources/account.js +173 -0
  107. package/dist/esm/resources/activity.d.ts +23 -0
  108. package/dist/esm/resources/activity.js +34 -0
  109. package/dist/esm/resources/api-key.d.ts +27 -0
  110. package/dist/esm/resources/api-key.js +41 -0
  111. package/dist/esm/resources/api-resource.d.ts +45 -0
  112. package/dist/esm/resources/api-resource.js +115 -0
  113. package/dist/esm/resources/charge.d.ts +154 -0
  114. package/dist/esm/resources/charge.js +215 -0
  115. package/dist/esm/resources/coupon.d.ts +35 -0
  116. package/dist/esm/resources/coupon.js +53 -0
  117. package/dist/esm/resources/event.d.ts +22 -0
  118. package/dist/esm/resources/event.js +31 -0
  119. package/dist/esm/resources/invoice.d.ts +68 -0
  120. package/dist/esm/resources/invoice.js +99 -0
  121. package/dist/esm/resources/member.d.ts +33 -0
  122. package/dist/esm/resources/member.js +49 -0
  123. package/dist/esm/resources/overview.d.ts +25 -0
  124. package/dist/esm/resources/overview.js +37 -0
  125. package/dist/esm/resources/payment-link.d.ts +65 -0
  126. package/dist/esm/resources/payment-link.js +92 -0
  127. package/dist/esm/resources/payout.d.ts +92 -0
  128. package/dist/esm/resources/payout.js +139 -0
  129. package/dist/esm/resources/pro-payment.d.ts +21 -0
  130. package/dist/esm/resources/pro-payment.js +31 -0
  131. package/dist/esm/resources/supported-asset.d.ts +36 -0
  132. package/dist/esm/resources/supported-asset.js +54 -0
  133. package/dist/esm/resources/webhook-endpoint.d.ts +93 -0
  134. package/dist/esm/resources/webhook-endpoint.js +151 -0
  135. package/dist/esm/services/account.d.ts +12 -0
  136. package/dist/esm/services/account.js +21 -0
  137. package/dist/esm/services/activity.d.ts +11 -0
  138. package/dist/esm/services/activity.js +13 -0
  139. package/dist/esm/services/api-keys.d.ts +17 -0
  140. package/dist/esm/services/api-keys.js +25 -0
  141. package/dist/esm/services/assets.d.ts +9 -0
  142. package/dist/esm/services/assets.js +9 -0
  143. package/dist/esm/services/billing.d.ts +8 -0
  144. package/dist/esm/services/billing.js +10 -0
  145. package/dist/esm/services/charges.d.ts +25 -0
  146. package/dist/esm/services/charges.js +45 -0
  147. package/dist/esm/services/coupons.d.ts +19 -0
  148. package/dist/esm/services/coupons.js +29 -0
  149. package/dist/esm/services/events.d.ts +13 -0
  150. package/dist/esm/services/events.js +17 -0
  151. package/dist/esm/services/invoices.d.ts +19 -0
  152. package/dist/esm/services/invoices.js +29 -0
  153. package/dist/esm/services/members.d.ts +26 -0
  154. package/dist/esm/services/members.js +40 -0
  155. package/dist/esm/services/overview.d.ts +7 -0
  156. package/dist/esm/services/overview.js +9 -0
  157. package/dist/esm/services/payment-links.d.ts +21 -0
  158. package/dist/esm/services/payment-links.js +33 -0
  159. package/dist/esm/services/payout-addresses.d.ts +12 -0
  160. package/dist/esm/services/payout-addresses.js +17 -0
  161. package/dist/esm/services/payouts.d.ts +15 -0
  162. package/dist/esm/services/payouts.js +21 -0
  163. package/dist/esm/services/service.d.ts +24 -0
  164. package/dist/esm/services/service.js +75 -0
  165. package/dist/esm/services/webhook-endpoints.d.ts +31 -0
  166. package/dist/esm/services/webhook-endpoints.js +57 -0
  167. package/dist/esm/version.d.ts +2 -0
  168. package/dist/esm/version.js +2 -0
  169. package/dist/esm/webhooks/webhook-event.d.ts +43 -0
  170. package/dist/esm/webhooks/webhook-event.js +84 -0
  171. package/dist/esm/webhooks/webhook.d.ts +36 -0
  172. package/dist/esm/webhooks/webhook.js +94 -0
  173. package/package.json +62 -5
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this library are documented here. It follows [Semantic Versioning](https://semver.org).
4
+
5
+ ## 0.1.1 - 2026-11-12
6
+
7
+ - The charges that `charges.create()`, `retrieve()`, `list()`, `iterator()`, `cancel()`, `recordRefund()`, `send()`, `accept()` and `simulate()` answer with are typed `ApiCharge`, whose `checkoutUrl` is a `string`, so `res.redirect(303, charge.checkoutUrl)` compiles under TypeScript's `strict`. Charges in webhooks and events keep `checkoutUrl: string | null`, since webhooks leave it out. Nothing changes at runtime.
8
+
9
+ ## 0.1.0 - 2026-11-12
10
+
11
+ The first version, covering what the PHP library covers as of its 0.6.5:
12
+
13
+ - `VeliraPayClient` with `account`, `assets`, `charges`, `paymentLinks`, `invoices`, `coupons`, `payouts`, `payoutAddresses`, `events`, `webhookEndpoints`, `overview`, `activity`, `apiKeys`, `members`, `invitations` and `billing`, each answering with typed objects that keep every attribute the API sent (`get()`, `toJSON()`).
14
+ - `Page` for lists, with `nextPage()` and `autoPagingIterator()`, and `iterator()` on every paged list for `for await`.
15
+ - An error class per kind of failure, all extending `VeliraPayError`: `AuthenticationError`, `PermissionError`, `NotFoundError`, `ConflictError`, `ValidationError`, `RateLimitError`, `ServerError`, `InvalidRequestError`, `UnexpectedResponseError`, `ConnectionError`, `SignatureVerificationError` and `InvalidArgumentError`.
16
+ - Retries of network errors, rate limits and outages, with an `Idempotency-Key` on every write so a retried one is carried out once.
17
+ - `Webhook.constructEvent()` and `constructEventFromRequest()`, which check the signature, the second one sent for a day after a secret is rotated, and its age.
18
+ - `Asset` also has `BTC_LIGHTNING`, `ETH_BASE`, `ETH_ARBITRUM` and `POL`, which the API's contract defines, and charges and coins read the `network_surcharge` the API sends, and charges its `payment_request`.
19
+ - ES module and CommonJS builds with their type definitions, for Node.js 22 or later, without dependencies.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VeliraPay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,270 @@
1
- # Temporary Holding Version
1
+ # VeliraPay for Node.js
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![Tests](https://github.com/VeliraPay/velirapay-node/actions/workflows/tests.yml/badge.svg)](https://github.com/VeliraPay/velirapay-node/actions/workflows/tests.yml)
4
+ [![Latest version](https://img.shields.io/npm/v/velirapay)](https://www.npmjs.com/package/velirapay)
5
+ [![License](https://img.shields.io/github/license/VeliraPay/velirapay-node)](LICENSE)
6
+
7
+ The official Node.js library for [VeliraPay](https://velirapay.com), the crypto payment processor. It covers the v1 API (the coins on offer, charges, invoices, payment links, coupons, payouts, events, webhook endpoints and your account) and verifies the webhooks VeliraPay sends you. It is written in TypeScript, ships its type definitions and has no dependencies.
8
+
9
+ - [Requirements](#requirements)
10
+ - [Installation](#installation)
11
+ - [Charging a customer](#charging-a-customer)
12
+ - [Verifying webhooks](#verifying-webhooks)
13
+ - [Which one to use](#which-one-to-use)
14
+ - [What the client covers](#what-the-client-covers)
15
+ - [Lists and pagination](#lists-and-pagination)
16
+ - [Errors](#errors)
17
+ - [Retries and idempotency](#retries-and-idempotency)
18
+ - [Test mode](#test-mode)
19
+ - [Configuration](#configuration)
20
+
21
+ The full API reference is at [velirapay.com/docs](https://velirapay.com/docs).
22
+
23
+ ## Requirements
24
+
25
+ Node.js 22 or later. The package works with both `import` and `require`.
26
+
27
+ ## Installation
28
+
29
+ ```bash
30
+ npm install velirapay
31
+ ```
32
+
33
+ ## Charging a customer
34
+
35
+ Create an API key in the dashboard under **Developers → API keys**. Keep it on your server: anyone holding it can create charges and read your payments.
36
+
37
+ To charge a customer, create a charge with what you already know about them, and send them to its checkout page:
38
+
39
+ ```js
40
+ import { VeliraPayClient } from 'velirapay';
41
+
42
+ const velirapay = new VeliraPayClient(process.env.VELIRAPAY_API_KEY);
43
+
44
+ const charge = await velirapay.charges.create({
45
+ amount: '49.90',
46
+ currency: 'EUR',
47
+ asset: 'BTC',
48
+ customer: { email: 'ada@example.com', name: 'Ada Lovelace' },
49
+ metadata: { order_id: '1042' },
50
+ success_url: 'https://example.com/orders/1042',
51
+ }, { idempotencyKey: 'order-1042' });
52
+
53
+ res.redirect(303, charge.checkoutUrl);
54
+ ```
55
+
56
+ They pay there, and VeliraPay tells your server about it with a [webhook](#verifying-webhooks). The exchange rate is locked when the charge is created, and the customer has 30 minutes to pay.
57
+
58
+ Parameters are sent as the API names them, in snake_case, and objects come back with camelCase properties: `charge.checkoutUrl`, `charge.assetAmount`, `charge.customer.ipAddress`. Amounts are always strings, such as `'49.90'`, never floating-point numbers, so no precision is lost. A charge the API answers with is an `ApiCharge`, whose `checkoutUrl` is always a string. Charges in webhooks leave it out, so theirs is `string | null`.
59
+
60
+ With CommonJS:
61
+
62
+ ```js
63
+ const { VeliraPayClient } = require('velirapay');
64
+ ```
65
+
66
+ A charge from your server comes from your server rather than the customer, so pass what you know about them as `customer` (`email`, `name`, `ip_address`, `user_agent`, `reference`, `phone`, `country` and `metadata`); it helps you spot fraud before you ship anything. `charge.customer` reads it back.
67
+
68
+ ## Verifying webhooks
69
+
70
+ Add an endpoint in the dashboard under **Developers → Webhooks** and copy its signing secret (`whsec_…`). `Webhook.constructEvent()` checks a delivery's signature and parses it. Pass it the **raw** body: a body parsed and encoded again no longer matches its signature. With Express:
71
+
72
+ ```js
73
+ import express from 'express';
74
+ import { SignatureVerificationError, Webhook } from 'velirapay';
75
+
76
+ const app = express();
77
+
78
+ app.post('/webhooks/velirapay', express.raw({ type: 'application/json' }), (req, res) => {
79
+ let event;
80
+
81
+ try {
82
+ event = Webhook.constructEvent(req.body, req.get(Webhook.SIGNATURE_HEADER), process.env.VELIRAPAY_WEBHOOK_SECRET);
83
+ } catch (error) {
84
+ if (error instanceof SignatureVerificationError) {
85
+ return res.sendStatus(400);
86
+ }
87
+
88
+ throw error;
89
+ }
90
+
91
+ // Sent when the endpoint is added or moved: it receives nothing else until it answers.
92
+ if (event.isVerification()) {
93
+ return res.type('application/json').send(event.challengeResponse());
94
+ }
95
+
96
+ // The same event can arrive more than once: remember this key, and skip the events you have handled.
97
+ const key = event.eventId ?? event.id;
98
+
99
+ if (event.is('charge.paid')) {
100
+ // Fulfil the order event.charge.metadata.order_id.
101
+ }
102
+
103
+ res.sendStatus(200);
104
+ });
105
+ ```
106
+
107
+ Where a framework hands you a Fetch API `Request`, as Next.js route handlers do, `await Webhook.constructEventFromRequest(request, secret)` reads the body and the header itself.
108
+
109
+ Some things to know:
110
+
111
+ - **Answer the verification challenge.** When an endpoint is added, or its URL changes, VeliraPay sends it a signed `webhook.verification` delivery and sends it nothing else until it answers with HTTP 200 and the challenge, as `event.challengeResponse()` gives it. Endpoint URLs must use `https://`.
112
+ - **A second signature for a day after a rotation.** For a day after you rotate the signing secret, the `X-VeliraPay-Signature` header carries a `v1` made with the old secret too, so a receiver that still has the old one keeps working. `constructEvent()` accepts a delivery whichever `v1` matches.
113
+ - **Signatures expire.** A signature more than 5 minutes from the current time is rejected, which stops an intercepted delivery from being replayed. Change the limit with the `tolerance` argument, in seconds; `0` turns the age check off.
114
+ - **Answer quickly.** A delivery counts as delivered when your endpoint answers with a 2xx status within 15 seconds, so queue slow work. Redirects are not followed.
115
+ - **Failed deliveries are retried.** A delivery is tried 14 times over about 3 days.
116
+ - **Skip duplicates by event.** A retried delivery keeps its id (`event.id`), but each endpoint gets its own copy of an event under its own id. `event.eventId` is the same on every copy, so `event.eventId ?? event.id` is the key to remember.
117
+ - **Fulfil once.** A paid invoice sends both `charge.paid`, for the charge that paid it, and `invoice.paid`. Fulfil on one of them.
118
+ - **Deliveries can arrive out of order.** When the order matters, fetch the current state with `velirapay.charges.retrieve(event.charge.id)`.
119
+
120
+ `charge.payment_detected` arrives as soon as a transfer is seen, before it is confirmed: tell the customer, but do not fulfil. It and `charge.late_payment` carry that transfer as `event.transaction`; `payout.sent` and `payout.confirmed` carry the payout as `event.payout`.
121
+
122
+ To test your handler, sign a payload yourself: `Webhook.signatureHeader(payload, 'whsec_test')` gives the header VeliraPay would send.
123
+
124
+ ## Which one to use
125
+
126
+ | When | Use | |
127
+ | --- | --- | --- |
128
+ | You are asking one customer for one payment | A charge | The default. It has one checkout page for that one payment, with the rate locked for 30 minutes. |
129
+ | The customer should choose the coin, or pays later | An invoice | It needs their name and email, has no timer, can be itemised and reminded, and lets them pick the coin on its page. |
130
+ | Many customers pay through the same public page | A payment link, created once | A product sold again and again, a tip jar, a link on your site. |
131
+
132
+ Do not create a new payment link for each customer or each order.
133
+
134
+ A charge needs its coin up front, because its exchange rate, amount and deposit address depend on it. Ask the customer on your own page, and offer them the coins from `velirapay.assets.list()`, which also gives the lowest price each can be charged at.
135
+
136
+ ## What the client covers
137
+
138
+ | Property | Methods |
139
+ | --- | --- |
140
+ | `charges` | `create`, `retrieve`, `list`, `iterator`, `send`, `cancel`, `accept`, `recordRefund`, `simulate` |
141
+ | `invoices` | `create`, `retrieve`, `list`, `iterator`, `send`, `void` |
142
+ | `paymentLinks` | `create`, `retrieve`, `update`, `list`, `iterator`, `archive`, `restore` |
143
+ | `coupons` | `create`, `retrieve`, `update`, `list`, `iterator`, `delete` |
144
+ | `assets` | `list` |
145
+ | `payouts` | `retrieve` (by the charge's id), `list`, `iterator`, `summary` |
146
+ | `payoutAddresses` | `list`, `update`, `delete` |
147
+ | `events` | `retrieve`, `list`, `iterator` |
148
+ | `webhookEndpoints` | `create`, `retrieve`, `update`, `delete`, `list`, `iterator`, `verify`, `test`, `rotateSecret`, `deliveries`, `retrieveDelivery`, `retryDelivery` |
149
+ | `account` | `retrieve`, `update`, `updatePaymentSettings` |
150
+ | `overview` | `retrieve` |
151
+ | `activity` | `list`, `iterator` |
152
+ | `apiKeys` | `create`, `update`, `revoke`, `list`, `iterator` |
153
+ | `members` | `update`, `remove`, `list`, `iterator` |
154
+ | `invitations` | `create`, `cancel`, `list`, `iterator` |
155
+ | `billing` | `startProPayment` |
156
+
157
+ Each takes the parameters the [API reference](https://velirapay.com/docs) gives, typed, and answers with an object such as a `Charge`. `get('field')` reads an attribute this version of the library does not know yet, and `JSON.stringify()` gives the object as the API sent it. Status values are in exported constants, such as `ChargeStatus.Paid` and `Asset.USDT`, and objects have helpers such as `charge.isPaid()`.
158
+
159
+ Most endpoints need a permission, which a key with full access has, except the sensitive ones, such as `webhook_endpoints:write` and `api_keys:write`: only a key made with them ticked has those. A key without the permission an endpoint needs gets a `PermissionError` naming it.
160
+
161
+ For an endpoint the library has no method for yet, send the request yourself: `await velirapay.request('GET', '/v1/charges', { per_page: 5 })` answers with the response, whose `json()` decodes it.
162
+
163
+ ## Lists and pagination
164
+
165
+ `list()` methods answer with one page, newest first (members oldest first). Pass `page` and `per_page` (up to 100, 25 by default):
166
+
167
+ ```js
168
+ const page = await velirapay.charges.list({ status: 'paid', per_page: 100 });
169
+
170
+ for (const charge of page) {
171
+ // …
172
+ }
173
+
174
+ page.total;
175
+ page.hasMore();
176
+ const next = await page.nextPage(); // null on the last page
177
+ ```
178
+
179
+ `iterator()` walks every page, fetching each one as it is reached:
180
+
181
+ ```js
182
+ for await (const charge of velirapay.charges.iterator({ status: 'paid' })) {
183
+ // …
184
+ }
185
+ ```
186
+
187
+ `assets.list()` answers with a single page holding every coin, and `payoutAddresses.list()` and `payouts.summary()` with arrays.
188
+
189
+ ## Errors
190
+
191
+ A failed request throws an error carrying the API's message. They are exported, and all extend `VeliraPayError`:
192
+
193
+ | Error | When |
194
+ | --- | --- |
195
+ | `AuthenticationError` | The API key is missing, wrong or revoked (401). |
196
+ | `PermissionError` | The API key may not do this (403): a restricted key without the permission the endpoint needs, or a key used from an IP address it does not allow. |
197
+ | `NotFoundError` | The object does not exist, or belongs to the other mode (404). |
198
+ | `ConflictError` | The object cannot make that change (409), such as canceling a charge that is no longer pending, or an idempotency key reused for a different request. |
199
+ | `ValidationError` | The request has invalid fields (422). `errors()` lists them by field, and `firstError('amount')` gives the first message for one. |
200
+ | `RateLimitError` | Too many requests (429). `retryAfter()` says how many seconds to wait. |
201
+ | `ServerError` | VeliraPay had a problem (5xx). |
202
+ | `InvalidRequestError` | Any other 4xx. |
203
+ | `ConnectionError` | The API could not be reached, after any retries. |
204
+ | `SignatureVerificationError` | A webhook's signature is missing, does not match, or is too old. |
205
+ | `InvalidArgumentError` | The library was given something it cannot use, such as an API key that does not start with `vp_live_` or `vp_test_`. |
206
+
207
+ Every error from an API answer extends `ApiError`, with `status`, `body` (the decoded response), `header(name)` and `retryAfter()`.
208
+
209
+ ```js
210
+ import { ApiError, ValidationError } from 'velirapay';
211
+
212
+ try {
213
+ await velirapay.charges.create({ amount: '0', currency: 'EUR', asset: 'BTC' });
214
+ } catch (error) {
215
+ if (error instanceof ValidationError) {
216
+ error.errors(); // { amount: ['…'] }
217
+ } else if (error instanceof ApiError) {
218
+ error.status;
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## Retries and idempotency
224
+
225
+ Requests that fail with a network error, a rate limit (429) or an outage (502, 503, 504) are tried again up to twice, waiting a little longer each time, or as long as the API's `Retry-After` header asks. A 500 is retried for reads only, since a write may have been carried out. A `Retry-After` longer than `maxRetryAfter` (10 seconds unless [configured](#configuration) otherwise) is not waited out: the error is thrown straight away, and its `retryAfter()` says how long to wait.
226
+
227
+ While retries are on, each write carries an `Idempotency-Key` header, so the API carries out a retried request once. The library makes up a key per call; pass your own, such as an order number, to make a call safe to repeat across processes:
228
+
229
+ ```js
230
+ const charge = await velirapay.charges.create(params, { idempotencyKey: 'order-1042' });
231
+ ```
232
+
233
+ For 24 hours, a request repeating a key gets the first successful response, and `velirapay.lastResponse()?.wasReplayed()` is then true. Reusing a key with a different body throws a `ConflictError`. A key covers one account, one mode, one method and one path, and is at most 255 characters.
234
+
235
+ ## Test mode
236
+
237
+ Keys starting with `vp_test_` work in test mode: charges are paid with test coins on each chain's test network, and nothing touches real funds. Test and live objects are fully separate, and each mode has its own webhooks.
238
+
239
+ ```js
240
+ velirapay.isTestMode(); // read from the key's prefix
241
+
242
+ // Pay a test charge without any coins: it becomes paid, and the webhooks go out as for a real payment.
243
+ await velirapay.charges.simulate('k3v9x2m7q8wz');
244
+ await velirapay.charges.simulate('k3v9x2m7q8wz', { amount: '0.001' }); // less than is due, to try an underpayment
245
+ ```
246
+
247
+ ## Configuration
248
+
249
+ ```js
250
+ const velirapay = new VeliraPayClient(process.env.VELIRAPAY_API_KEY, {
251
+ maxRetries: 2, // 0 turns retries off
252
+ maxRetryAfter: 10, // the longest Retry-After, in seconds, waited out before trying again
253
+ timeout: 30, // seconds an attempt may take
254
+ appInfo: 'MyShop/2.1', // added to the User-Agent
255
+ fetch: globalThis.fetch,
256
+ });
257
+ ```
258
+
259
+ `fetch` takes any implementation of the Fetch API, such as one that goes through a proxy or one that answers your tests.
260
+
261
+ ## Development
262
+
263
+ ```bash
264
+ npm install
265
+ npm run check # types, the build and the tests
266
+ ```
267
+
268
+ ## License
269
+
270
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,94 @@
1
+ import { Mode } from './enums.ts';
2
+ import type { ApiResponse } from './http/api-response.ts';
3
+ import { type Fetch } from './http/transport.ts';
4
+ import type { RequestOptions } from './params.ts';
5
+ import { AccountService } from './services/account.ts';
6
+ import { ActivityService } from './services/activity.ts';
7
+ import { ApiKeyService } from './services/api-keys.ts';
8
+ import { AssetService } from './services/assets.ts';
9
+ import { BillingService } from './services/billing.ts';
10
+ import { ChargeService } from './services/charges.ts';
11
+ import { CouponService } from './services/coupons.ts';
12
+ import { EventService } from './services/events.ts';
13
+ import { InvoiceService } from './services/invoices.ts';
14
+ import { InvitationService, MemberService } from './services/members.ts';
15
+ import { OverviewService } from './services/overview.ts';
16
+ import { PaymentLinkService } from './services/payment-links.ts';
17
+ import { PayoutAddressService } from './services/payout-addresses.ts';
18
+ import { PayoutService } from './services/payouts.ts';
19
+ import { WebhookEndpointService } from './services/webhook-endpoints.ts';
20
+ /** How the client reaches the API. */
21
+ export interface ClientOptions {
22
+ /** The API's address; https://api.velirapay.com unless given. */
23
+ baseUrl?: string;
24
+ /** How many times a request is tried again after a network error, a rate limit, an outage, or while one with the same idempotency key is still being processed; 0 turns retries off. 2 unless given. */
25
+ maxRetries?: number;
26
+ /** The longest Retry-After, in seconds, that is waited out before trying again; a longer one is thrown straight away. 10 unless given. */
27
+ maxRetryAfter?: number;
28
+ /** How many seconds an attempt may take. 30 unless given. */
29
+ timeout?: number;
30
+ /** Your application's name and version, added to the User-Agent. */
31
+ appInfo?: string;
32
+ /** The fetch implementation to send requests with; the global one unless given. */
33
+ fetch?: Fetch;
34
+ }
35
+ /** A client for the VeliraPay API, authenticated with one API key. */
36
+ export declare class VeliraPayClient {
37
+ #private;
38
+ /** The version of this library. */
39
+ static readonly VERSION = "0.1.1";
40
+ /** The address of the VeliraPay API. */
41
+ static readonly DEFAULT_BASE_URL = "https://api.velirapay.com";
42
+ /** The account the API key belongs to. */
43
+ readonly account: AccountService;
44
+ /** Assets: the coins on offer, and the lowest price each can be charged at. */
45
+ readonly assets: AssetService;
46
+ /** Charges: how to ask a customer for a payment, in one coin. */
47
+ readonly charges: ChargeService;
48
+ /** Payment links: reusable checkout pages many customers pay through. */
49
+ readonly paymentLinks: PaymentLinkService;
50
+ /** Invoices: itemised bills customers pay in the coin of their choice. */
51
+ readonly invoices: InvoiceService;
52
+ /** Coupons: discount codes for the hosted checkout and new charges. */
53
+ readonly coupons: CouponService;
54
+ /** Payouts: what became of each paid charge. */
55
+ readonly payouts: PayoutService;
56
+ /** Events: the history of every charge and invoice. */
57
+ readonly events: EventService;
58
+ /** Webhook endpoints: where webhooks are delivered, and whether they can be. */
59
+ readonly webhookEndpoints: WebhookEndpointService;
60
+ /** The overview: the dashboard's figures for the last 30 days. */
61
+ readonly overview: OverviewService;
62
+ /** Payout addresses: where the account's payouts go on each network. */
63
+ readonly payoutAddresses: PayoutAddressService;
64
+ /** The activity log: everything done in the account, and who did it. */
65
+ readonly activity: ActivityService;
66
+ /** API keys: the account's keys, never reaching further than the one asking. */
67
+ readonly apiKeys: ApiKeyService;
68
+ /** Members: the people in the account and their roles. */
69
+ readonly members: MemberService;
70
+ /** Invitations: who has been asked to join and has not yet. */
71
+ readonly invitations: InvitationService;
72
+ /** Billing: paying for Pro in crypto. */
73
+ readonly billing: BillingService;
74
+ /**
75
+ * Create a new client.
76
+ *
77
+ * @param apiKey A secret key from the dashboard, starting with "vp_live_" or "vp_test_". Keep it on your server.
78
+ */
79
+ constructor(apiKey: string | undefined, options?: ClientOptions);
80
+ /** The mode the API key works in, read from its prefix. */
81
+ mode(): Mode;
82
+ /** Whether the API key moves real funds. */
83
+ isLiveMode(): boolean;
84
+ /** Whether the API key works on test networks. */
85
+ isTestMode(): boolean;
86
+ /** The response to the most recent request this client made, such as to read its headers; with requests running side by side, whichever finished last. */
87
+ lastResponse(): ApiResponse | null;
88
+ /**
89
+ * Send a request to an endpoint this library has no method for yet.
90
+ *
91
+ * @param params The query string for GET requests, the JSON body otherwise.
92
+ */
93
+ request(method: string, path: string, params?: object, options?: RequestOptions): Promise<ApiResponse>;
94
+ }
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VeliraPayClient = void 0;
4
+ const enums_ts_1 = require("./enums.js");
5
+ const errors_ts_1 = require("./errors.js");
6
+ const transport_ts_1 = require("./http/transport.js");
7
+ const account_ts_1 = require("./services/account.js");
8
+ const activity_ts_1 = require("./services/activity.js");
9
+ const api_keys_ts_1 = require("./services/api-keys.js");
10
+ const assets_ts_1 = require("./services/assets.js");
11
+ const billing_ts_1 = require("./services/billing.js");
12
+ const charges_ts_1 = require("./services/charges.js");
13
+ const coupons_ts_1 = require("./services/coupons.js");
14
+ const events_ts_1 = require("./services/events.js");
15
+ const invoices_ts_1 = require("./services/invoices.js");
16
+ const members_ts_1 = require("./services/members.js");
17
+ const overview_ts_1 = require("./services/overview.js");
18
+ const payment_links_ts_1 = require("./services/payment-links.js");
19
+ const payout_addresses_ts_1 = require("./services/payout-addresses.js");
20
+ const payouts_ts_1 = require("./services/payouts.js");
21
+ const webhook_endpoints_ts_1 = require("./services/webhook-endpoints.js");
22
+ const version_ts_1 = require("./version.js");
23
+ /** A client for the VeliraPay API, authenticated with one API key. */
24
+ class VeliraPayClient {
25
+ /** The version of this library. */
26
+ static VERSION = version_ts_1.VERSION;
27
+ /** The address of the VeliraPay API. */
28
+ static DEFAULT_BASE_URL = 'https://api.velirapay.com';
29
+ /** The account the API key belongs to. */
30
+ account;
31
+ /** Assets: the coins on offer, and the lowest price each can be charged at. */
32
+ assets;
33
+ /** Charges: how to ask a customer for a payment, in one coin. */
34
+ charges;
35
+ /** Payment links: reusable checkout pages many customers pay through. */
36
+ paymentLinks;
37
+ /** Invoices: itemised bills customers pay in the coin of their choice. */
38
+ invoices;
39
+ /** Coupons: discount codes for the hosted checkout and new charges. */
40
+ coupons;
41
+ /** Payouts: what became of each paid charge. */
42
+ payouts;
43
+ /** Events: the history of every charge and invoice. */
44
+ events;
45
+ /** Webhook endpoints: where webhooks are delivered, and whether they can be. */
46
+ webhookEndpoints;
47
+ /** The overview: the dashboard's figures for the last 30 days. */
48
+ overview;
49
+ /** Payout addresses: where the account's payouts go on each network. */
50
+ payoutAddresses;
51
+ /** The activity log: everything done in the account, and who did it. */
52
+ activity;
53
+ /** API keys: the account's keys, never reaching further than the one asking. */
54
+ apiKeys;
55
+ /** Members: the people in the account and their roles. */
56
+ members;
57
+ /** Invitations: who has been asked to join and has not yet. */
58
+ invitations;
59
+ /** Billing: paying for Pro in crypto. */
60
+ billing;
61
+ #mode;
62
+ #transport;
63
+ /**
64
+ * Create a new client.
65
+ *
66
+ * @param apiKey A secret key from the dashboard, starting with "vp_live_" or "vp_test_". Keep it on your server.
67
+ */
68
+ constructor(apiKey, options = {}) {
69
+ if (typeof apiKey !== 'string' || apiKey.trim() === '') {
70
+ throw new errors_ts_1.InvalidArgumentError('A VeliraPay API key is required. Create one in the dashboard under Developers > API keys.');
71
+ }
72
+ const mode = Object.values(enums_ts_1.Mode).find((candidate) => apiKey.startsWith(`vp_${candidate}_`));
73
+ if (mode === undefined) {
74
+ throw new errors_ts_1.InvalidArgumentError('A VeliraPay API key starts with "vp_live_" or "vp_test_". Copy yours from the dashboard under Developers > API keys.');
75
+ }
76
+ if (!/^[\x21-\x7e]+$/.test(apiKey)) {
77
+ throw new errors_ts_1.InvalidArgumentError('The API key holds a character no key has, such as a space or a line break. Check how it is loaded.');
78
+ }
79
+ const { baseUrl = VeliraPayClient.DEFAULT_BASE_URL, maxRetries = 2, maxRetryAfter = 10, timeout = 30, appInfo } = options;
80
+ const fetch = options.fetch ?? globalThis.fetch;
81
+ if (!Number.isInteger(maxRetries) || maxRetries < 0) {
82
+ throw new errors_ts_1.InvalidArgumentError('The number of retries must be a whole number, 0 or more.');
83
+ }
84
+ if (!(maxRetryAfter >= 0)) {
85
+ throw new errors_ts_1.InvalidArgumentError('The longest Retry-After to wait out cannot be negative.');
86
+ }
87
+ if (!(timeout > 0) || !Number.isFinite(timeout)) {
88
+ throw new errors_ts_1.InvalidArgumentError('The timeout must be a number of seconds above 0.');
89
+ }
90
+ if (!/^https?:\/\/[^\s/]+/i.test(baseUrl) || !URL.canParse(baseUrl)) {
91
+ throw new errors_ts_1.InvalidArgumentError('The base URL must be an http or https address.');
92
+ }
93
+ if (typeof fetch !== 'function') {
94
+ throw new errors_ts_1.InvalidArgumentError('No fetch implementation was found. Use Node.js 22 or later, or pass one as the fetch option.');
95
+ }
96
+ let userAgent = `VeliraPay-Node/${version_ts_1.VERSION} Node/${globalThis.process?.versions?.node ?? 'unknown'}`;
97
+ if (appInfo !== undefined && appInfo.trim() !== '') {
98
+ if (!/^[\x20-\x7e]+$/.test(appInfo)) {
99
+ throw new errors_ts_1.InvalidArgumentError('The app info can only hold printable ASCII characters, as it is sent in the User-Agent header.');
100
+ }
101
+ userAgent += ` ${appInfo.trim()}`;
102
+ }
103
+ this.#mode = mode;
104
+ this.#transport = new transport_ts_1.HttpTransport({ apiKey, fetch, baseUrl, maxRetries, maxRetryAfter, timeout, userAgent });
105
+ this.account = new account_ts_1.AccountService(this.#transport);
106
+ this.assets = new assets_ts_1.AssetService(this.#transport);
107
+ this.charges = new charges_ts_1.ChargeService(this.#transport);
108
+ this.paymentLinks = new payment_links_ts_1.PaymentLinkService(this.#transport);
109
+ this.invoices = new invoices_ts_1.InvoiceService(this.#transport);
110
+ this.coupons = new coupons_ts_1.CouponService(this.#transport);
111
+ this.payouts = new payouts_ts_1.PayoutService(this.#transport);
112
+ this.events = new events_ts_1.EventService(this.#transport);
113
+ this.webhookEndpoints = new webhook_endpoints_ts_1.WebhookEndpointService(this.#transport);
114
+ this.overview = new overview_ts_1.OverviewService(this.#transport);
115
+ this.payoutAddresses = new payout_addresses_ts_1.PayoutAddressService(this.#transport);
116
+ this.activity = new activity_ts_1.ActivityService(this.#transport);
117
+ this.apiKeys = new api_keys_ts_1.ApiKeyService(this.#transport);
118
+ this.members = new members_ts_1.MemberService(this.#transport);
119
+ this.invitations = new members_ts_1.InvitationService(this.#transport);
120
+ this.billing = new billing_ts_1.BillingService(this.#transport);
121
+ }
122
+ /** The mode the API key works in, read from its prefix. */
123
+ mode() {
124
+ return this.#mode;
125
+ }
126
+ /** Whether the API key moves real funds. */
127
+ isLiveMode() {
128
+ return this.#mode === enums_ts_1.Mode.Live;
129
+ }
130
+ /** Whether the API key works on test networks. */
131
+ isTestMode() {
132
+ return this.#mode === enums_ts_1.Mode.Test;
133
+ }
134
+ /** The response to the most recent request this client made, such as to read its headers; with requests running side by side, whichever finished last. */
135
+ lastResponse() {
136
+ return this.#transport.lastResponse();
137
+ }
138
+ /**
139
+ * Send a request to an endpoint this library has no method for yet.
140
+ *
141
+ * @param params The query string for GET requests, the JSON body otherwise.
142
+ */
143
+ request(method, path, params = {}, options = {}) {
144
+ if (method.toUpperCase() === 'GET') {
145
+ return this.#transport.request('GET', path, params);
146
+ }
147
+ return this.#transport.request(method, path, {}, Object.keys(params).length === 0 ? undefined : params, options.idempotencyKey);
148
+ }
149
+ }
150
+ exports.VeliraPayClient = VeliraPayClient;