@myzonerocks/pact 0.1.7 → 0.1.8
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/dist/src/adapters/mpesa.js +6 -19
- package/dist/test/mpesa.test.js +7 -3
- package/package.json +1 -1
- package/src/adapters/mpesa.ts +6 -21
|
@@ -153,12 +153,12 @@ export class MpesaLeg {
|
|
|
153
153
|
},
|
|
154
154
|
];
|
|
155
155
|
}
|
|
156
|
-
// The
|
|
157
|
-
// or
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
156
|
+
// The STK push fixed the amount we authorized; a payer approves that exact amount
|
|
157
|
+
// or cancels, so an approved collection is always the full authorized amount. The
|
|
158
|
+
// authenticated query above confirms success, so the settled amount is the one we
|
|
159
|
+
// recorded — it is never read from the unsigned callback, since a forged callback
|
|
160
|
+
// (the checkout id is not a secret) could otherwise carry a wrong amount and block
|
|
161
|
+
// a collection the payer completed. The receipt is an audit reference only.
|
|
162
162
|
const receipt = metadataString(cb.CallbackMetadata?.Item ?? [], "MpesaReceiptNumber");
|
|
163
163
|
return [
|
|
164
164
|
{
|
|
@@ -185,19 +185,6 @@ function wholeShillings(m) {
|
|
|
185
185
|
}
|
|
186
186
|
return Number(amount);
|
|
187
187
|
}
|
|
188
|
-
// metadataInt pulls a named numeric value out of the callback metadata items,
|
|
189
|
-
// used to read the paid Amount. M-Pesa amounts are whole shillings but may arrive
|
|
190
|
-
// as a number with a fractional part, so it is floored to the shilling.
|
|
191
|
-
function metadataInt(items, name) {
|
|
192
|
-
for (const item of items) {
|
|
193
|
-
if (item.Name !== name) {
|
|
194
|
-
continue;
|
|
195
|
-
}
|
|
196
|
-
const n = Number(item.Value);
|
|
197
|
-
return Number.isFinite(n) ? Math.floor(n) : 0;
|
|
198
|
-
}
|
|
199
|
-
return 0;
|
|
200
|
-
}
|
|
201
188
|
// metadataString pulls a named string value out of the callback metadata items.
|
|
202
189
|
function metadataString(items, name) {
|
|
203
190
|
for (const item of items) {
|
package/dist/test/mpesa.test.js
CHANGED
|
@@ -201,13 +201,17 @@ describe("mpesa pay-in", () => {
|
|
|
201
201
|
const events = await leg.parseWebhook(successCallback(collected.providerRef, "FORGEDRCPT"), {});
|
|
202
202
|
expect(events).toHaveLength(0);
|
|
203
203
|
});
|
|
204
|
-
it("
|
|
204
|
+
it("a forged callback amount cannot block a confirmed collection", async () => {
|
|
205
205
|
const api = new FakeDaraja(counter("tx"));
|
|
206
206
|
const leg = buildLeg(api);
|
|
207
207
|
const q = quote({ srcAmount: kes("5000"), fees: kes("0") });
|
|
208
208
|
const collected = await leg.collect("intent-1", q, emptyAuth, "0711000111");
|
|
209
|
-
// Daraja confirms success
|
|
209
|
+
// Daraja's authenticated query confirms success; the callback body claims a
|
|
210
|
+
// different (forged) amount, which must be ignored — the collection still settles.
|
|
210
211
|
api.queryResults.set(collected.providerRef, { resultCode: 0, resultDesc: "", pending: false });
|
|
211
|
-
await
|
|
212
|
+
const events = await leg.parseWebhook(successCallback(collected.providerRef, "QGR7XYZ123"), {});
|
|
213
|
+
expect(events).toHaveLength(1);
|
|
214
|
+
expect(events[0].state).toBe(State.Settled);
|
|
215
|
+
expect(events[0].providerTxRef).toBe("QGR7XYZ123");
|
|
212
216
|
});
|
|
213
217
|
});
|
package/package.json
CHANGED
package/src/adapters/mpesa.ts
CHANGED
|
@@ -270,13 +270,12 @@ export class MpesaLeg implements PayInLeg, PayOutLeg {
|
|
|
270
270
|
},
|
|
271
271
|
];
|
|
272
272
|
}
|
|
273
|
-
// The
|
|
274
|
-
// or
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
273
|
+
// The STK push fixed the amount we authorized; a payer approves that exact amount
|
|
274
|
+
// or cancels, so an approved collection is always the full authorized amount. The
|
|
275
|
+
// authenticated query above confirms success, so the settled amount is the one we
|
|
276
|
+
// recorded — it is never read from the unsigned callback, since a forged callback
|
|
277
|
+
// (the checkout id is not a secret) could otherwise carry a wrong amount and block
|
|
278
|
+
// a collection the payer completed. The receipt is an audit reference only.
|
|
280
279
|
const receipt = metadataString(cb.CallbackMetadata?.Item ?? [], "MpesaReceiptNumber");
|
|
281
280
|
return [
|
|
282
281
|
{
|
|
@@ -321,20 +320,6 @@ function wholeShillings(m: Money): number {
|
|
|
321
320
|
return Number(amount);
|
|
322
321
|
}
|
|
323
322
|
|
|
324
|
-
// metadataInt pulls a named numeric value out of the callback metadata items,
|
|
325
|
-
// used to read the paid Amount. M-Pesa amounts are whole shillings but may arrive
|
|
326
|
-
// as a number with a fractional part, so it is floored to the shilling.
|
|
327
|
-
function metadataInt(items: StkCallbackItem[], name: string): number {
|
|
328
|
-
for (const item of items) {
|
|
329
|
-
if (item.Name !== name) {
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
const n = Number(item.Value);
|
|
333
|
-
return Number.isFinite(n) ? Math.floor(n) : 0;
|
|
334
|
-
}
|
|
335
|
-
return 0;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
323
|
// metadataString pulls a named string value out of the callback metadata items.
|
|
339
324
|
function metadataString(items: StkCallbackItem[], name: string): string {
|
|
340
325
|
for (const item of items) {
|