Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/cli/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,29 @@ describe('production mode', () => {
expect(merchantRequests[1].headers.authorization).toMatch(/^Payment /);
});

it('exits 1 when the paid retry fails', async () => {
setNextResponse(200, APPROVED_SPT_REQUEST);
setMerchantResponse(402, '{"error":"payment required"}', {
'www-authenticate': WWW_AUTHENTICATE_STRIPE,
});
setMerchantResponse(401, '{"error":"spt rejected"}');

const result = await runProdCli(
'mpp',
'pay',
`http://127.0.0.1:${merchantPort}/api/charge`,
'--spend-request-id',
'lsrq_spt_001',
'--output-json',
);

expect(result.exitCode).toBe(1);
const err = parseJson(result.stderr) as { error: string };
expect(err.error).toContain('Payment submission failed with status 401');
expect(err.error).toContain('spt rejected');
expect(merchantRequests).toHaveLength(2);
});

it('passthrough: no 402 returns response without signing', async () => {
setNextResponse(200, APPROVED_SPT_REQUEST);
setMerchantResponse(200, '{"ok":true}');
Expand Down
53 changes: 20 additions & 33 deletions packages/cli/src/commands/mpp/pay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ function buildHeaders(
return result;
}

async function readPayResult(
response: Response,
options?: { failOnError?: boolean },
): Promise<PayResult> {
const responseHeaders = Object.fromEntries(response.headers.entries());
const body = await response.text();

if (options?.failOnError && !response.ok) {
throw new Error(
`Payment submission failed with status ${response.status}: ${body}`,
);
}

return { status: response.status, headers: responseHeaders, body };
}

export async function runMppPay(
url: string,
spendRequestId: string,
Expand Down Expand Up @@ -84,11 +100,7 @@ export async function runMppPay(

// 4. If not 402, return as-is
if (initialResponse.status !== 402) {
const responseHeaders = Object.fromEntries(
initialResponse.headers.entries(),
);
const body = await initialResponse.text();
return { status: initialResponse.status, headers: responseHeaders, body };
return readPayResult(initialResponse);
}

// 5. Parse challenges, find stripe
Expand Down Expand Up @@ -123,16 +135,7 @@ export async function runMppPay(
},
});

if (!retryResponse.ok) {
const body = await retryResponse.text();
outputError(
`Payment submission failed with status ${retryResponse.status}: ${body}`,
);
}

const responseHeaders = Object.fromEntries(retryResponse.headers.entries());
const body = await retryResponse.text();
return { status: retryResponse.status, headers: responseHeaders, body };
return readPayResult(retryResponse, { failOnError: true });
}

type Step = 'retrieving' | 'probing' | 'signing' | 'submitting' | 'done';
Expand Down Expand Up @@ -196,15 +199,7 @@ export function MppPay({
});

if (initialResponse.status !== 402) {
const responseHeaders = Object.fromEntries(
initialResponse.headers.entries(),
);
const body = await initialResponse.text();
setResult({
status: initialResponse.status,
headers: responseHeaders,
body,
});
setResult(await readPayResult(initialResponse));
setStep('done');
onComplete();
return;
Expand Down Expand Up @@ -241,15 +236,7 @@ export function MppPay({
},
});

const responseHeaders = Object.fromEntries(
retryResponse.headers.entries(),
);
const body = await retryResponse.text();
setResult({
status: retryResponse.status,
headers: responseHeaders,
body,
});
setResult(await readPayResult(retryResponse, { failOnError: true }));
setStep('done');
onComplete();
} catch (err) {
Expand Down
Loading