cybersource-brunei-payauth 0.2.2 → 0.2.4
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 +25 -3
- package/package.json +2 -2
- package/src/client.js +10 -1
- package/src/httpClient.js +42 -5
- package/src/payments.js +62 -13
- package/src/statusDescriptions.js +1 -1
package/README.md
CHANGED
|
@@ -232,7 +232,7 @@ const outcome = await client.voidTransaction(paymentId, { reason: 'customer canc
|
|
|
232
232
|
```
|
|
233
233
|
|
|
234
234
|
- **`operation: 'reversal'`** — the authorization hadn't been captured yet, so this released the hold via an authorization reversal (`POST /pts/v2/payments/{id}/reversals`). The reversed amount is read automatically from Cybersource's own record of the authorized amount, not re-derived or guessed.
|
|
235
|
-
- **`operation: 'capture-void'`** — a capture already existed, so this voided *that* instead (`POST /pts/v2/captures/{id}/voids`). Cybersource typically batches captures to the processor once a day, so this only works same-day; after that, use
|
|
235
|
+
- **`operation: 'capture-void'`** — a capture already existed, so this voided *that* instead (`POST /pts/v2/captures/{id}/voids`). Cybersource typically batches captures to the processor once a day, so this only works same-day; after that, use one of the refund methods below.
|
|
236
236
|
|
|
237
237
|
`voidTransaction` assumes the only follow-on transaction it will ever see against one of its own authorizations is this package's own `capture()` call — if Cybersource reports *any* related transaction, it voids that one. If your integration creates other follow-on transactions against the same authorization outside this package (e.g. from the Cybersource Business Center), call the lower-level functions yourself instead:
|
|
238
238
|
|
|
@@ -244,7 +244,29 @@ await client.voidPayment(paymentId, { totalAmount, currency, reason, clientRefer
|
|
|
244
244
|
await client.voidCapture(captureId, { clientReferenceCode }); // post-capture
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
`voidPayment`'s `totalAmount`/`currency` must match the *authorized* amount from `authorize()`'s own response — not the amount you originally requested, which Cybersource notes can differ (e.g. partial approvals). It throws immediately, before any network call, if either is omitted, rather than sending Cybersource a request that would be declined anyway.
|
|
247
|
+
`voidPayment`'s `totalAmount`/`currency` must match the *authorized* amount from `authorize()`'s own response — not the amount you originally requested, which Cybersource notes can differ (e.g. partial approvals). It throws immediately, before any network call, if either is omitted, rather than sending Cybersource a request that would be declined anyway.
|
|
248
|
+
|
|
249
|
+
## Refunding a settled payment
|
|
250
|
+
|
|
251
|
+
CyberSource provides two refund endpoints based on how the original payment was captured. Both methods support full or partial refunds; pass the amount to return, using the original transaction's currency:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
// Use only when authorization and capture were combined in the original POST /pts/v2/payments.
|
|
255
|
+
await client.refundPayment(paymentId, {
|
|
256
|
+
totalAmount: '50.00',
|
|
257
|
+
currency: 'BND',
|
|
258
|
+
clientReferenceCode: 'refund-order-1', // optional
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Use when capture() was called separately. Pass capture()'s response id, not paymentId.
|
|
262
|
+
await client.refundCapture(captureId, {
|
|
263
|
+
totalAmount: '20.00',
|
|
264
|
+
currency: 'BND',
|
|
265
|
+
clientReferenceCode: 'partial-refund-order-1', // optional
|
|
266
|
+
});
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`refundPayment` calls `POST /pts/v2/payments/{paymentId}/refunds`; `refundCapture` calls `POST /pts/v2/captures/{captureId}/refunds`. Both require `totalAmount` and `currency` and return CyberSource's response unchanged, including the refund transaction `id` and `status`.
|
|
248
270
|
|
|
249
271
|
### Plain-language status descriptions
|
|
250
272
|
|
|
@@ -263,7 +285,7 @@ describeStatus('PENDING_AUTHENTICATION');
|
|
|
263
285
|
// bank) before the charge can proceed."
|
|
264
286
|
```
|
|
265
287
|
|
|
266
|
-
It covers every documented status value across `authorize()`, `capture()`, `voidPayment()`, and `
|
|
288
|
+
It covers every documented status value across `authorize()`, `capture()`, `voidPayment()`, `voidCapture()`, `refundPayment()`, and `refundCapture()`. `checkTransaction()`'s result already includes this as `statusDescription`, computed from its own `status` field. An unrecognized status (or `null`/missing) returns a safe fallback sentence rather than throwing — this is a plain-language convenience, not a validator.
|
|
267
289
|
|
|
268
290
|
## BIN Lookup
|
|
269
291
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cybersource-brunei-payauth",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Cybersource payer-authentication (3-D Secure)
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Cybersource payer-authentication (3-D Secure) and payments client for Brunei merchants, including payment and capture refunds.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"index.js",
|
package/src/client.js
CHANGED
|
@@ -5,7 +5,14 @@ const {
|
|
|
5
5
|
validateStepUp,
|
|
6
6
|
} = require("./payerAuth");
|
|
7
7
|
const { buildStepUpFormHtml } = require("./stepUpForm");
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
authorize,
|
|
10
|
+
capture,
|
|
11
|
+
voidPayment,
|
|
12
|
+
voidCapture,
|
|
13
|
+
refundPayment,
|
|
14
|
+
refundCapture,
|
|
15
|
+
} = require("./payments");
|
|
9
16
|
const { storeInstrument, getInstrument, deleteInstrument } = require("./tms");
|
|
10
17
|
const { startPayment, finishPayment } = require("./facade");
|
|
11
18
|
const { lookupBin } = require("./binLookup");
|
|
@@ -23,6 +30,8 @@ function createCyberSourceClient(config) {
|
|
|
23
30
|
capture: (paymentId, payload) => capture(httpClient, paymentId, payload),
|
|
24
31
|
voidPayment: (paymentId, payload) => voidPayment(httpClient, paymentId, payload),
|
|
25
32
|
voidCapture: (captureId, payload) => voidCapture(httpClient, captureId, payload),
|
|
33
|
+
refundPayment: (paymentId, payload) => refundPayment(httpClient, paymentId, payload),
|
|
34
|
+
refundCapture: (captureId, payload) => refundCapture(httpClient, captureId, payload),
|
|
26
35
|
storeInstrument: (payload) => storeInstrument(httpClient, payload),
|
|
27
36
|
getInstrument: (payload) => getInstrument(httpClient, payload),
|
|
28
37
|
deleteInstrument: (payload) => deleteInstrument(httpClient, payload),
|
package/src/httpClient.js
CHANGED
|
@@ -20,6 +20,43 @@ function createHttpClient({
|
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function extractCybersourceError(parsed, status) {
|
|
24
|
+
if (parsed?.errorInformation) {
|
|
25
|
+
return {
|
|
26
|
+
reason: parsed.errorInformation.reason || "UNKNOWN_ERROR",
|
|
27
|
+
|
|
28
|
+
message:
|
|
29
|
+
parsed.errorInformation.message ||
|
|
30
|
+
`Cybersource request failed with status ${status}`,
|
|
31
|
+
|
|
32
|
+
details: {
|
|
33
|
+
...parsed.errorInformation,
|
|
34
|
+
transactionId: parsed.id,
|
|
35
|
+
clientReferenceInformation: parsed.clientReferenceInformation,
|
|
36
|
+
status: parsed.status,
|
|
37
|
+
rawResponse: parsed,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (parsed?.errors?.length) {
|
|
43
|
+
return {
|
|
44
|
+
reason: parsed.errors[0]?.reason || "UNKNOWN_ERROR",
|
|
45
|
+
message:
|
|
46
|
+
parsed.errors[0]?.message ||
|
|
47
|
+
`Cybersource request failed with status ${status}`,
|
|
48
|
+
details: parsed.errors,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
reason: parsed?.reason || "UNKNOWN_ERROR",
|
|
54
|
+
message:
|
|
55
|
+
parsed?.message || `Cybersource request failed with status ${status}`,
|
|
56
|
+
details: parsed?.details || parsed,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
23
60
|
async function request(method, path, body) {
|
|
24
61
|
const date = new Date().toUTCString();
|
|
25
62
|
const signatureHeaders = buildSignatureHeaders({
|
|
@@ -69,13 +106,13 @@ function createHttpClient({
|
|
|
69
106
|
}
|
|
70
107
|
|
|
71
108
|
if (!response.ok) {
|
|
109
|
+
const error = extractCybersourceError(parsed, response.status);
|
|
110
|
+
|
|
72
111
|
throw new CyberSourceApiError({
|
|
73
112
|
status: response.status,
|
|
74
|
-
reason:
|
|
75
|
-
message:
|
|
76
|
-
|
|
77
|
-
`Cybersource request failed with status ${response.status}`,
|
|
78
|
-
details: parsed.details || parsed,
|
|
113
|
+
reason: error.reason,
|
|
114
|
+
message: error.message,
|
|
115
|
+
details: error.details,
|
|
79
116
|
});
|
|
80
117
|
}
|
|
81
118
|
|
package/src/payments.js
CHANGED
|
@@ -51,11 +51,11 @@ async function authorize(
|
|
|
51
51
|
saveCard = false,
|
|
52
52
|
},
|
|
53
53
|
) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
if (!authenticationResult) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"authorize requires authenticationResult: no 3-D Secure authentication result was provided",
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
59
|
|
|
60
60
|
// Cybersource requires processingInformation.commerceIndicator on every payer-authenticated
|
|
61
61
|
// authorization — it does not derive it from consumerAuthenticationInformation server-side, and
|
|
@@ -228,9 +228,9 @@ async function voidPayment(
|
|
|
228
228
|
// Reverses an already-settled capture (POST /pts/v2/captures/{id}/voids), before it reaches the
|
|
229
229
|
// processor — Cybersource typically batches captures to the processor once a day, so this only
|
|
230
230
|
// works same-day. Takes the capture()'s own id, not the original authorization's paymentId.
|
|
231
|
-
async function voidCapture(
|
|
232
|
-
httpClient,
|
|
233
|
-
captureId,
|
|
231
|
+
async function voidCapture(
|
|
232
|
+
httpClient,
|
|
233
|
+
captureId,
|
|
234
234
|
{ clientReferenceCode } = {},
|
|
235
235
|
) {
|
|
236
236
|
assertSafeId(captureId, "captureId");
|
|
@@ -242,8 +242,57 @@ async function voidCapture(
|
|
|
242
242
|
}
|
|
243
243
|
: undefined,
|
|
244
244
|
};
|
|
245
|
-
|
|
246
|
-
return httpClient.post(`/pts/v2/captures/${captureId}/voids`, body);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
|
|
245
|
+
|
|
246
|
+
return httpClient.post(`/pts/v2/captures/${captureId}/voids`, body);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildRefundBody({ currency, totalAmount, clientReferenceCode } = {}) {
|
|
250
|
+
if (!totalAmount || !currency) {
|
|
251
|
+
throw new Error("Refund requires totalAmount and currency");
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
clientReferenceInformation: clientReferenceCode
|
|
256
|
+
? {
|
|
257
|
+
code: clientReferenceCode,
|
|
258
|
+
}
|
|
259
|
+
: undefined,
|
|
260
|
+
|
|
261
|
+
orderInformation: {
|
|
262
|
+
amountDetails: {
|
|
263
|
+
totalAmount,
|
|
264
|
+
currency,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Use this only when authorization and capture were combined in authorize(). The id is the
|
|
271
|
+
// payment id returned by that combined operation, as required by Cybersource's payment-scoped
|
|
272
|
+
// refund endpoint.
|
|
273
|
+
async function refundPayment(httpClient, paymentId, payload) {
|
|
274
|
+
assertSafeId(paymentId, "paymentId");
|
|
275
|
+
return httpClient.post(
|
|
276
|
+
`/pts/v2/payments/${paymentId}/refunds`,
|
|
277
|
+
buildRefundBody(payload),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Use this when capture() was called separately. Cybersource requires the capture response's id,
|
|
282
|
+
// not the original authorization's payment id, for this endpoint.
|
|
283
|
+
async function refundCapture(httpClient, captureId, payload) {
|
|
284
|
+
assertSafeId(captureId, "captureId");
|
|
285
|
+
return httpClient.post(
|
|
286
|
+
`/pts/v2/captures/${captureId}/refunds`,
|
|
287
|
+
buildRefundBody(payload),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
module.exports = {
|
|
292
|
+
authorize,
|
|
293
|
+
capture,
|
|
294
|
+
voidPayment,
|
|
295
|
+
voidCapture,
|
|
296
|
+
refundPayment,
|
|
297
|
+
refundCapture,
|
|
298
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Cybersource's own status vocabulary, spelled out in plain language for people who don't work
|
|
2
2
|
// with payment processing day to day. Covers every status value documented for the response types
|
|
3
3
|
// this package returns: authorize() (Payments API), capture(), voidPayment() (Authorization
|
|
4
|
-
// Reversal),
|
|
4
|
+
// Reversal), voidCapture() (Capture Void), refundPayment(), and refundCapture().
|
|
5
5
|
const STATUS_DESCRIPTIONS = {
|
|
6
6
|
AUTHORIZED:
|
|
7
7
|
"The bank approved the charge. The money is held on the customer's card, but the merchant hasn't taken it yet — that happens on capture.",
|