@secureprocessing/node 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -4,21 +4,24 @@ Card payments for Belize. Zero dependencies; runs on Node 20+, Deno, Bun and Clo
4
4
 
5
5
  ## Install
6
6
 
7
- From our own domain — this file is the npm package itself:
7
+ ```bash
8
+ npm install @secureprocessing/node@0.2.1
9
+ ```
10
+
11
+ The same version is served from our own domain, byte for byte — npm publishes the very file at this
12
+ URL, so either one records the same hash in your lockfile:
8
13
 
9
14
  ```bash
10
- npm install https://secureprocessing.app/sdk/secureprocessing-node-0.2.0.tgz
15
+ npm install https://secureprocessing.app/sdk/secureprocessing-node-0.2.1.tgz
11
16
  ```
12
17
 
13
- It installs as `@secureprocessing/node`, so every `import ... from "@secureprocessing/node"` below
14
- works unchanged, and your lockfile pins its hash. `pnpm add` takes the same URL. Once the package is
15
- on the npm registry, `npm install @secureprocessing/node@0.2.0` gives you these same bytes;
16
- [the docs](https://docs.secureprocessing.app/sdk) say which of the two to use today.
18
+ It installs as `@secureprocessing/node` either way, so every `import ... from "@secureprocessing/node"`
19
+ below works unchanged. `pnpm add` takes the same name or URL.
17
20
 
18
21
  Deno, including Supabase Edge Functions:
19
22
 
20
23
  ```ts
21
- import { SecureProcessing } from "https://secureprocessing.app/sdk/0.2.0/index.js";
24
+ import { SecureProcessing } from "https://secureprocessing.app/sdk/0.2.1/index.js";
22
25
 
23
26
  // Deno 2 imports only from its built-in allow-list unless told otherwise:
24
27
  // deno run --allow-net --allow-env --allow-import=secureprocessing.app your-script.ts
@@ -153,6 +156,13 @@ try {
153
156
  (on the bank's page, or in 3-D Secure). Nothing was changed, the message says when to try again,
154
157
  and `err.isPaymentInProgress` is `true`. The client never retries it — it is minutes, not a backoff.
155
158
 
159
+ `400 refund_not_yet_possible` (`err.isRefundNotYetPossible`) is a partial refund on a payment the
160
+ bank has not settled yet. The bank definitely refused and nothing was refunded. It is **the one
161
+ error to retry under a NEW idempotency key**, once the payment has settled — usually by the next
162
+ business day: the original key stays bound to this answer for 24 hours and would only replay it.
163
+ Calling the method again WITHOUT `idempotencyKey` already sends a fresh key, so that is all the
164
+ retry needs. If nothing has been refunded yet, a full refund can be made now.
165
+
156
166
  🔴 `500 outcome_unknown` (`err.isOutcomeUnknown`) means the request reached the bank and the result
157
167
  could not be confirmed: **the money may have moved.** Never repeat it under a new idempotency key —
158
168
  that is a second charge or refund. Retrying under the same key (`err.idempotencyKey`, which the
@@ -231,11 +241,15 @@ whatever today's is.
231
241
 
232
242
  Keys beginning `sk_test_` never move real money. Test cards:
233
243
 
234
- | Number | Result |
235
- |---|---|
236
- | `4000001111111118` | Succeeds |
237
- | `4444444411111111` | Declined |
238
- | `5555555555555599` | 3-D Secure challenge |
244
+ | Number | Expiry | Result |
245
+ |---|---|---|
246
+ | `5555555555555599` | 12/34 | Succeeds |
247
+ | `4111111111111111` | 12/26 | Succeeds |
248
+ | `5168494895055780` | 12/26 | Declined |
249
+
250
+ CVC `123` for all of them. These are the bank's sandbox cards as they stood when this version was
251
+ released, and two of them expire 12/26; the current list is always at
252
+ <https://docs.secureprocessing.app/test-cards>.
239
253
 
240
254
  ## Options
241
255
 
package/dist/errors.d.ts CHANGED
@@ -33,6 +33,10 @@ export declare class SecureProcessingError extends Error {
33
33
  * 🔴 To retry a failed POST, pass THIS back: `{ idempotencyKey: err.idempotencyKey }`. Calling the
34
34
  * method again without it sends a new key, and a new key is a new request to the API: if the
35
35
  * first one did reach the bank, that is a second charge or refund.
36
+ *
37
+ * The one exception is `isRefundNotYetPossible`: the bank definitely refused and nothing moved,
38
+ * so the retry after settlement takes a NEW key — this one stays bound to the refusal for 24 hours
39
+ * and replays it. Calling the method again WITHOUT `idempotencyKey` already sends a fresh key.
36
40
  */
37
41
  readonly idempotencyKey: string | undefined;
38
42
  /**
@@ -70,6 +74,15 @@ export declare class SecureProcessingError extends Error {
70
74
  * The client never retries it: a loop here would only poll the bank on the customer's behalf.
71
75
  */
72
76
  get isPaymentInProgress(): boolean;
77
+ /**
78
+ * `400 refund_not_yet_possible`: a partial refund on a payment the bank has not settled yet. The
79
+ * bank refunds only settled payments and cannot reverse part of one. Nothing was refunded. Retry
80
+ * once it has settled — usually by the next business day — with a NEW idempotency key: this
81
+ * answer is stored against the old one for 24 hours and replayed. Calling the method again
82
+ * WITHOUT `idempotencyKey` already sends a fresh key. A new key is safe for this error only. If
83
+ * nothing has been refunded yet, a full refund can be made now.
84
+ */
85
+ get isRefundNotYetPossible(): boolean;
73
86
  /**
74
87
  * 🔴 `500 outcome_unknown`: the request asked the bank to move money — a charge, a capture, a
75
88
  * refund — and the API could not confirm what happened. The money MAY HAVE MOVED.
package/dist/errors.js CHANGED
@@ -21,6 +21,10 @@ export class SecureProcessingError extends Error {
21
21
  * 🔴 To retry a failed POST, pass THIS back: `{ idempotencyKey: err.idempotencyKey }`. Calling the
22
22
  * method again without it sends a new key, and a new key is a new request to the API: if the
23
23
  * first one did reach the bank, that is a second charge or refund.
24
+ *
25
+ * The one exception is `isRefundNotYetPossible`: the bank definitely refused and nothing moved,
26
+ * so the retry after settlement takes a NEW key — this one stays bound to the refusal for 24 hours
27
+ * and replays it. Calling the method again WITHOUT `idempotencyKey` already sends a fresh key.
24
28
  */
25
29
  idempotencyKey;
26
30
  /**
@@ -80,6 +84,17 @@ export class SecureProcessingError extends Error {
80
84
  get isPaymentInProgress() {
81
85
  return this.code === "payment_in_progress";
82
86
  }
87
+ /**
88
+ * `400 refund_not_yet_possible`: a partial refund on a payment the bank has not settled yet. The
89
+ * bank refunds only settled payments and cannot reverse part of one. Nothing was refunded. Retry
90
+ * once it has settled — usually by the next business day — with a NEW idempotency key: this
91
+ * answer is stored against the old one for 24 hours and replayed. Calling the method again
92
+ * WITHOUT `idempotencyKey` already sends a fresh key. A new key is safe for this error only. If
93
+ * nothing has been refunded yet, a full refund can be made now.
94
+ */
95
+ get isRefundNotYetPossible() {
96
+ return this.code === "refund_not_yet_possible";
97
+ }
83
98
  /**
84
99
  * 🔴 `500 outcome_unknown`: the request asked the bank to move money — a charge, a capture, a
85
100
  * refund — and the API could not confirm what happened. The money MAY HAVE MOVED.
package/dist/index.d.ts CHANGED
@@ -76,6 +76,9 @@ export declare class SecureProcessing {
76
76
  * against what is refundable — and wait for `refund.succeeded` or `refund.failed`. A thrown
77
77
  * error with `isOutcomeUnknown` means the same without a refund object to hold: list the
78
78
  * payment's refunds to see it, and never retry under a new idempotency key.
79
+ *
80
+ * A partial refund on a payment the bank has not settled yet throws with
81
+ * `isRefundNotYetPossible`. That one is retried after settlement under a NEW key.
79
82
  */
80
83
  create: (params: CreateRefundParams, options?: RequestOptions) => Promise<ApiResponse<Refund>>;
81
84
  retrieve: (id: string) => Promise<ApiResponse<Refund>>;
@@ -237,6 +240,9 @@ export declare class SecureProcessing {
237
240
  * 🔴 `amount_paid` does NOT change — the payment happened, and the refund is its own record, so
238
241
  * the excess is netted against anything already refunded. Calling this twice returns an error
239
242
  * rather than refunding twice. Returns the refund, not the invoice.
243
+ *
244
+ * If the overpaying payment has not been settled by the bank yet, this throws with
245
+ * `isRefundNotYetPossible`. That one is retried after settlement under a NEW key.
240
246
  */
241
247
  refundOverpayment: (id: string, options?: RequestOptions) => Promise<ApiResponse<Refund>>;
242
248
  /**
package/dist/index.js CHANGED
@@ -73,6 +73,9 @@ export class SecureProcessing {
73
73
  * against what is refundable — and wait for `refund.succeeded` or `refund.failed`. A thrown
74
74
  * error with `isOutcomeUnknown` means the same without a refund object to hold: list the
75
75
  * payment's refunds to see it, and never retry under a new idempotency key.
76
+ *
77
+ * A partial refund on a payment the bank has not settled yet throws with
78
+ * `isRefundNotYetPossible`. That one is retried after settlement under a NEW key.
76
79
  */
77
80
  create: (params, options) => this.http.post("/v1/refunds", params, options),
78
81
  retrieve: (id) => this.http.get(`/v1/refunds/${encodeURIComponent(id)}`),
@@ -209,6 +212,9 @@ export class SecureProcessing {
209
212
  * 🔴 `amount_paid` does NOT change — the payment happened, and the refund is its own record, so
210
213
  * the excess is netted against anything already refunded. Calling this twice returns an error
211
214
  * rather than refunding twice. Returns the refund, not the invoice.
215
+ *
216
+ * If the overpaying payment has not been settled by the bank yet, this throws with
217
+ * `isRefundNotYetPossible`. That one is retried after settlement under a NEW key.
212
218
  */
213
219
  refundOverpayment: (id, options) => this.http.post(`/v1/invoices/${encodeURIComponent(id)}/refund_overpayment`, {}, options),
214
220
  /**
package/dist/version.d.ts CHANGED
@@ -5,4 +5,4 @@
5
5
  * filesystem. `test/sdk.test.ts` fails if the two disagree — 0.1.0's User-Agent was a literal in two
6
6
  * places, and a bump would have left both claiming the old version.
7
7
  */
8
- export declare const VERSION = "0.2.0";
8
+ export declare const VERSION = "0.2.1";
package/dist/version.js CHANGED
@@ -6,4 +6,4 @@
6
6
  * filesystem. `test/sdk.test.ts` fails if the two disagree — 0.1.0's User-Agent was a literal in two
7
7
  * places, and a bump would have left both claiming the old version.
8
8
  */
9
- export const VERSION = "0.2.0";
9
+ export const VERSION = "0.2.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secureprocessing/node",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Secure Processing API client. Card payments for Belize.",
5
5
  "homepage": "https://docs.secureprocessing.app/sdk",
6
6
  "bugs": {