77 lines
4.9 KiB
JavaScript
77 lines
4.9 KiB
JavaScript
// In-memory regression checks; no real payment or network requests.
|
|
const assert = require('node:assert/strict');
|
|
const { fixture } = require('./check-api.cjs');
|
|
|
|
(async () => {
|
|
const priced = fixture(); priced.info.status = 'purchasable';
|
|
priced.info.priceFen = 600; priced.info.productId = 'configured-product';
|
|
await priced.client.refresh();
|
|
priced.info.priceFen = 1200; // Purchase must refresh instead of using the previous view.
|
|
assert.equal((await priced.client.request('purchase', { periodId: priced.periodId })).code, 1);
|
|
const pricedOrder = priced.calls.find(c => c.path.includes('orderPaySig'));
|
|
assert.equal(pricedOrder.body.itemPrice, 1200);
|
|
assert.equal(pricedOrder.body.itemid, 'configured-product');
|
|
assert.equal(pricedOrder.body.itemCount, 1);
|
|
for (const price of [undefined, null, 0, -1, 1.5, '300']) {
|
|
const invalidPrice = fixture(); invalidPrice.info.status = 'purchasable'; invalidPrice.info.priceFen = price;
|
|
assert.equal((await invalidPrice.client.request('purchase', { periodId: invalidPrice.periodId })).code, 0);
|
|
assert(!invalidPrice.calls.some(c => c.path.includes('orderPaySig')));
|
|
}
|
|
console.log('PASS: order uses freshly fetched product and price; invalid prices cannot create orders.');
|
|
for (const ios of [false, true]) {
|
|
const f = fixture(new Map(), { ios, cancelPay: true });
|
|
f.info.status = 'purchasable';
|
|
await f.client.request('purchase', { periodId: f.periodId });
|
|
const original = { ...f.client.ledger.orders[f.periodId] };
|
|
f.server.cancelPay = false;
|
|
f.server.intercept = (path, body, ok, fail, reply) => {
|
|
if (path.includes('orderPaySig')) {
|
|
ok({ outTradeNo: 'new-order', signData: 'untouched-signature', signature: 's', paySig: 'p' });
|
|
return true;
|
|
}
|
|
};
|
|
const before = f.calls.length;
|
|
const result = await f.client.request('purchase', { periodId: f.periodId });
|
|
assert.equal(result.code, 1);
|
|
assert.equal(f.counts().payCalls, 2);
|
|
assert.equal(f.counts().saves, 0);
|
|
const creates = f.calls.filter(c => c.path.includes('orderPaySig'));
|
|
assert.notEqual(creates[1].body.createRequestId, original.createRequestId);
|
|
assert.equal(creates[1].body.itemCount, 1); assert.equal(creates[1].body.itemPrice, 300);
|
|
assert.equal(f.client.ledger.orders[f.periodId].outTradeNo, 'new-order');
|
|
assert(!f.calls.slice(before).some(c => c.body.outTradeNo === original.outTradeNo));
|
|
}
|
|
console.log('PASS: Android and iOS cancellation retries create a fresh request and pay the new order.');
|
|
|
|
for (const paymentState of ['cancelled', 'failed', 'pending', undefined]) {
|
|
const f = fixture(); f.info.status = 'purchasable';
|
|
f.client.ledger.orders[f.periodId] = { outTradeNo: 'old-order', createRequestId: 'original', channel: 'native', ios: false, paymentState };
|
|
assert.equal((await f.client.request('purchase', { periodId: f.periodId })).code, 1);
|
|
assert.equal(f.counts().payCalls, 1);
|
|
assert(f.calls.some(c => c.path.includes('orderPaySig') && c.body.createRequestId !== 'original'));
|
|
assert(!f.calls.some(c => c.body.outTradeNo === 'old-order'));
|
|
assert(f.calls.some(c => c.path.includes('getPayInfo') && c.body.outTradeNo === 'order-1'));
|
|
}
|
|
console.log('PASS: every purchase creates a new request regardless of previous state; only the new order is queried.');
|
|
const f = fixture(); f.info.status = 'purchasable';
|
|
f.client.ledger.orders[f.periodId] = { outTradeNo: 'original-order', createRequestId: 'original', channel: 'native', ios: false, paymentState: 'cancelled' };
|
|
await f.client.refresh();
|
|
assert(!f.calls.some(c => c.path.includes('getPayInfo')));
|
|
const result = await f.client.request('purchase', { periodId: f.periodId });
|
|
assert.equal(result.code, 1);
|
|
assert.equal(f.counts().payCalls, 1);
|
|
assert.equal(f.client.ledger.orders[f.periodId].outTradeNo, 'order-1');
|
|
assert.notEqual(f.client.ledger.orders[f.periodId].createRequestId, 'original');
|
|
assert(!f.calls.some(c => c.body.outTradeNo === 'original-order'));
|
|
console.log('PASS: stored cancellation no longer queries or requires reuse of the cancelled order.');
|
|
const invalid = fixture(); invalid.info.status = 'purchasable';
|
|
invalid.server.intercept = (path, body, ok) => {
|
|
if (path.includes('orderPaySig')) { ok({ outTradeNo: 'invalid-1', signData: '', signature: 's', paySig: 'p' }); return true; }
|
|
};
|
|
const rejected = await invalid.client.request('purchase', { periodId: invalid.periodId });
|
|
assert.equal(rejected.code, 0); assert.match(rejected.msg, /signData/);
|
|
assert.equal(invalid.counts().payCalls, 0);
|
|
assert.equal(invalid.client.ledger.orders[invalid.periodId].outTradeNo, 'invalid-1');
|
|
console.log('PASS: incomplete payment parameters are rejected while preserving the order.');
|
|
})().catch(error => { console.error(error); process.exitCode = 1; });
|