fix(security): unique payment reference_id, reject replayed callbacks (H3)

reference_id (the gateway's settled-transaction ref) was not unique, so the
same successful callback — or a RefNum replayed onto another order — could
credit twice. Add a unique index (NULL until success, so pending/failed rows
don't collide) and an application-level pre-check in the callback that fails the
payment if the reference already belongs to another order. The unique index is
the hard backstop behind the check.

Regression: PaymentCallbackAmountTest::testReplayedGatewayReferenceIsRejected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-06-28 19:08:26 +03:30
co-authored by Claude Opus 4.8
parent aa87b4a9cb
commit 7fa4b55d3f
7 changed files with 80 additions and 2 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ class Payment
#[ORM\Column(name: 'gateway_token', type: 'string', length: 255, nullable: true)]
private ?string $gatewayToken = null;
#[ORM\Column(name: 'reference_id', type: 'string', length: 255, nullable: true)]
#[ORM\Column(name: 'reference_id', type: 'string', length: 255, nullable: true, unique: true)]
private ?string $referenceId = null;
#[ORM\Column(name: 'frontend_address', type: 'string', length: 500, nullable: true)]