53 lines
1.8 KiB
TypeScript
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')
|
|
}
|
|
})
|