d3ro-voice/server/supabase/functions/payple-webhook/index.test.ts
2026-08-29 18:33:45 +09:00

53 lines
1.8 KiB
TypeScript

import {
classifyPaypleWebhook,
validateReconciledPaypleEvent,
} from './index.ts'
import { PaypleVerificationError } from '../_shared/payple.ts'
function assert(condition: boolean, message: string): asserts condition {
if (!condition) throw new Error(message)
}
Deno.test('Payple webhook classifies only documented state-changing events', () => {
assert(classifyPaypleWebhook({ PCD_PAY_WORK: 'PUSERDEL' }) === 'billing_key_revoked', 'key revoke')
assert(classifyPaypleWebhook({ PCD_PAY_CODE: 'PAYC0000' }) === 'cancellation', 'cancel')
assert(classifyPaypleWebhook({
PCD_PAY_RST: 'success', PCD_PAY_OID: 'D3RO-20260821153045-user-nonce',
}) === 'payment', 'payment')
assert(classifyPaypleWebhook({ PCD_PAY_RST: 'error' }) === 'unsupported', 'failure ignored')
})
Deno.test('Payple webhook payload cannot override the official lookup result', () => {
const payload = {
PCD_PAY_RST: 'success',
PCD_PAY_OID: 'D3RO-20260821153045-user-nonce',
PCD_PAY_TYPE: 'card',
PCD_PAYER_ID: 'payer-verified',
PCD_PAY_TOTAL: '9900',
}
const lookup = {
PCD_PAY_RST: 'success' as const,
PCD_PAY_CODE: 'PCHK0000',
PCD_PAY_MSG: '결제 완료',
PCD_PAY_OID: payload.PCD_PAY_OID,
PCD_PAY_TYPE: 'card' as const,
PCD_PAYER_ID: 'payer-verified',
PCD_PAY_TOTAL: '9900',
PCD_PAY_TIME: '20260821153045',
}
validateReconciledPaypleEvent(payload, lookup)
for (const tampered of [
{ ...payload, PCD_PAY_OID: 'D3RO-20260821153045-other-nonce' },
{ ...payload, PCD_PAYER_ID: 'payer-attacker' },
{ ...payload, PCD_PAY_TOTAL: '29900' },
{ ...payload, PCD_PAY_TYPE: 'transfer' },
]) {
let error: unknown
try {
validateReconciledPaypleEvent(tampered, lookup)
} catch (caught) {
error = caught
}
assert(error instanceof PaypleVerificationError, 'tampered webhook must fail closed')
}
})