Page MenuHomePhabricator
Paste P89567

GravyWebhookProcessing-Capture.puml
ActivePublic

Authored by jgleeson on Mar 2 2026, 7:31 PM.
Project Tags
None
Referenced Files
F72476100: GravyWebhookProcessing-Capture.puml
Mar 2 2026, 7:43 PM
F72476008: GravyWebhookProcessing-Capture.puml
Mar 2 2026, 7:31 PM
Subscribers
None
@startuml
autonumber
title
<b>Gravy Webhook Processing - Capture (SmashPig)</b>
end title
participant "Gravy" as API
boundary "RequestHandler" as RH
participant "GravyListener" as GL
participant "ListenerValidator" as LV
participant "TransactionMessage" as TM
participant "TransactionAction" as TA
participant "TransactionDetails\nNormalizer" as TDN
participant "QueueWrapper" as QW
participant "JobQueueConsumer" as JQC
participant "RecordCaptureJob" as RCJ
participant "PendingDatabase" as PDB
== Webhook: transaction.captured (capture_succeeded) ==
note over RH, PDB
Gravy sends a second webhook after the capture succeeds.
SmashPig creates a job to reconcile the capture with the pending donor record.
end note
=== Phase 1: Webhook Receipt & Job Creation ===
[-> RH: POST /index.php?p=gravy/listener\n(target.type=transaction,\nstatus=capture_succeeded)
activate RH
RH -> GL: GravyListener::execute(...)
activate GL
GL -> LV: validateWebhookEventHeader(headers, config)
activate LV
LV --> GL: Validation OK
deactivate LV
GL -> GL: json_decode(rawBody)
GL -> GL: mapFromWebhookMessage(parsed)\n-> "TransactionMessage"
GL -> TM: TransactionMessage::init()
activate TM
TM --> GL: TransactionMessage
deactivate TM
GL -> TA: TransactionAction::execute(message)
activate TA
TA -> TDN: getTransactionDetails(msg)
activate TDN
TDN -> TDN: getResponseMapperForPaymentMethod("card")\n-> ResponseMapper (via config mappers/cc-response)
TDN -> TDN: mapFromPaymentResponse()\nnormalizeStatus("capture_succeeded")\n-> COMPLETE
TDN --> TA: PaymentProviderExtendedResponse\n(status=COMPLETE, isSuccessful=true)
deactivate TDN
TA -> TA: status == COMPLETE\n-> RecordCaptureJob::factory(msg, details)
TA -> QW: QueueWrapper::push("jobs-gravy",\nRecordCaptureJob payload)
activate QW
QW --> TA: pushed
deactivate QW
TA --> GL: return true
deactivate TA
GL --> RH: return true
deactivate GL
RH -->]: HTTP 200 OK
deactivate RH
=== Phase 2: Job Processing (async, from queue consumer) ===
JQC -> QW: Dequeue job from "jobs-gravy"
activate JQC
activate QW
QW --> JQC: RecordCaptureJob payload
deactivate QW
JQC -> RCJ: RecordCaptureJob::execute()
activate RCJ
RCJ -> PDB: fetchMessageByGatewayOrderId(\n"gravy", orderId)
activate PDB
PDB --> RCJ: result
deactivate PDB
alt Pending record found (happy path)
RCJ -> RCJ: addMissingFieldsToPendingRecord()
RCJ -> API: getFullTransactionDetails(gateway_txn_id)\n(calls getLatestPaymentStatus internally)
activate API
API --> RCJ: Full transaction response\n(backend_processor, payment_submethod,\ndonor details, recurring_payment_token)
deactivate API
note right of RCJ
Enrich pending record with:
- gateway_txn_id
- backend_processor
- payment_submethod
- backend_processor_txn_id
- payment_orchestrator_reconciliation_id
- donor name, email, address
- recurring_payment_token (if recurring)
end note
RCJ -> RCJ: Set date = strtotime(eventDate)
RCJ -> QW: QueueWrapper::push("donations",\nenrichedMessage)
activate QW
QW --> RCJ: pushed
deactivate QW
RCJ -> PDB: markMessageResolved(message)\n(UPDATE pending SET is_resolved = 1)
activate PDB
PDB --> RCJ: resolved
deactivate PDB
RCJ --> JQC: return true (success)
else Pending record NOT found
RCJ -> RCJ: Log warning:\n"Could not find pending record\nfor gateway_txn_id / order_id"
RCJ --> JQC: return true (no donation pushed)
end
deactivate RCJ
deactivate JQC
@enduml