55interface
66
77uses
8- Classes, SysUtils, fpjson;
8+ Classes, SysUtils, fpjson, yookassa_api
9+ ;
910
1011type
1112 { Event handler for logging }
@@ -20,17 +21,42 @@ EYooKassaWebhookError = class(Exception)
2021 property ErrorCode: string read FErrorCode;
2122 end ;
2223
24+ { Webhook object types based on event analysis }
25+ TYookassaWebhookObjectType = (
26+ wotUnknown,
27+ wotPayment,
28+ wotRefund,
29+ wotPayout,
30+ wotDeal,
31+ wotPaymentMethod
32+ );
33+
2334 { TYookassaWebhookData }
2435 TYookassaWebhookData = class (TPersistent)
36+ private
37+ FPaymentResponse: TYookassaPaymentResponse;
38+ FObjectType: TYookassaWebhookObjectType;
39+ FRaw: TJSONObject;
40+ function GetPaymentResponse : TYookassaPaymentResponse;
41+ function DetermineObjectType (const aEvent: string): TYookassaWebhookObjectType;
2542 public
2643 Event: string; // payment.succeeded, payment.waiting_for_capture, etc.
27- ObjectType: string; // payment, refund, payout, etc.
28- ObjectId: string; // Object ID (e.g. pay_123)
29- Status: string; // Object status
30- Raw: TJSONObject; // Full JSON for direct access
44+
3145 function Clone : TYookassaWebhookData;
3246 constructor Create(aRaw: TJSONObject);
3347 destructor Destroy; override;
48+
49+ property ObjectType: TYookassaWebhookObjectType read FObjectType; // Determined from event
50+ { Typed access to webhook objects }
51+ property PaymentResponse: TYookassaPaymentResponse read GetPaymentResponse;
52+ property Raw: TJSONObject read FRaw; // Full JSON for direct access
53+
54+ { TODO: Add support for other webhook object types
55+ property RefundResponse: TYookassaRefundResponse read GetRefundResponse;
56+ property PayoutResponse: TYookassaPayoutResponse read GetPayoutResponse;
57+ property DealResponse: TYookassaDealResponse read GetDealResponse;
58+ property PaymentMethodResponse: TYookassaPaymentMethodResponse read GetPaymentMethodResponse;
59+ }
3460 end ;
3561
3662 { Event handler type for webhook events }
@@ -43,6 +69,12 @@ TYookassaWebhookHandler = class
4369 FOnPaymentWaitingForCapture: TYookassaWebhookEvent;
4470 FOnPaymentCanceled: TYookassaWebhookEvent;
4571 FOnRefundSucceeded: TYookassaWebhookEvent;
72+ { TODO: Add handlers for other events
73+ FOnPayoutSucceeded: TYookassaWebhookEvent;
74+ FOnPayoutCanceled: TYookassaWebhookEvent;
75+ FOnDealClosed: TYookassaWebhookEvent;
76+ FOnPaymentMethodActive: TYookassaWebhookEvent;
77+ }
4678 FOnLog: TYookassaLogEvent;
4779 procedure Log (aEventType: TEventType; const aMsg: string);
4880 public
@@ -58,6 +90,9 @@ TYookassaWebhookHandler = class
5890
5991implementation
6092
93+ uses
94+ StrUtils;
95+
6196{ EYooKassaWebhookError }
6297
6398constructor EYooKassaWebhookError.Create(const aMsg, aErrorCode: string);
@@ -68,26 +103,55 @@ constructor EYooKassaWebhookError.Create(const aMsg, aErrorCode: string);
68103
69104{ TYookassaWebhookData }
70105
106+ function TYookassaWebhookData.DetermineObjectType (const aEvent: string): TYookassaWebhookObjectType;
107+ begin
108+ if StartsStr(' payment.' , aEvent) then
109+ Result := wotPayment
110+ else if StartsStr(' refund.' , aEvent) then
111+ Result := wotRefund
112+ else if StartsStr(' payout.' , aEvent) then
113+ Result := wotPayout
114+ else if StartsStr(' deal.' , aEvent) then
115+ Result := wotDeal
116+ else if StartsStr(' payment_method.' , aEvent) then
117+ Result := wotPaymentMethod
118+ else
119+ Result := wotUnknown;
120+ end ;
121+
122+ function TYookassaWebhookData.GetPaymentResponse : TYookassaPaymentResponse;
123+ var
124+ aObjectJSON: TJSONObject;
125+ begin
126+ if not Assigned(FPaymentResponse) and (ObjectType = wotPayment) then
127+ begin
128+ aObjectJSON := Raw.Find(' object' ) as TJSONObject;
129+ if Assigned(aObjectJSON) then
130+ FPaymentResponse := TYookassaPaymentResponse.Create(aObjectJSON.Clone as TJSONObject);
131+ end ;
132+ Result := FPaymentResponse;
133+ end ;
134+
71135function TYookassaWebhookData.Clone : TYookassaWebhookData;
72136begin
73- Result:= TYookassaWebhookData.Create(Raw.Clone as TJSONObject);
137+ Result := TYookassaWebhookData.Create(Raw.Clone as TJSONObject);
74138end ;
75139
76140constructor TYookassaWebhookData.Create(aRaw: TJSONObject);
77141var
78142 aObj: TJSONObject;
79143begin
80- Raw := aRaw;
144+ FRaw := aRaw;
81145 aObj := aRaw.Find(' object' ) as TJSONObject;
82- Event := aRaw.Get(' event' , ' ' );
83- ObjectType := aObj.Get(' type' , ' ' );
84- ObjectId := aObj.Get(' id' , ' ' );
85- Status := aObj.Get(' status' , ' ' );
146+
147+ Event := aRaw.Get(' event' , EmptyStr);
148+ FObjectType := DetermineObjectType(Event);
86149end ;
87150
88151destructor TYookassaWebhookData.Destroy;
89152begin
90- Raw.Free;
153+ FPaymentResponse.Free;
154+ FRaw.Free;
91155 inherited Destroy;
92156end ;
93157
@@ -126,33 +190,42 @@ function TYookassaWebhookHandler.HandleWebhook(const aRawBody: string): string;
126190 aData := TYookassaWebhookData.Create(aJSON);
127191 try
128192 aEventType := aData.Event;
129- Log(etInfo, Format(' Webhook: Processing event "%s" for object "%s" (status: %s)' ,
130- [aEventType, aData.ObjectId, aData.Status]));
193+ Log(etInfo, Format(' Webhook: Processing event "%s"' , [aEventType]));
131194
132195 // 3. Call the appropriate event handler
133- if Assigned(FOnPaymentSucceeded) and (aEventType = ' payment.succeeded' ) then
134- begin
135- Log(etInfo, ' Webhook: Triggering OnPaymentSucceeded' );
136- FOnPaymentSucceeded(aData);
137- end
138- else if Assigned(FOnPaymentWaitingForCapture) and (aEventType = ' payment.waiting_for_capture' ) then
139- begin
140- Log(etInfo, ' Webhook: Triggering OnPaymentWaitingForCapture' );
141- FOnPaymentWaitingForCapture(aData);
142- end
143- else if Assigned(FOnPaymentCanceled) and (aEventType = ' payment.canceled' ) then
144- begin
145- Log(etInfo, ' Webhook: Triggering OnPaymentCanceled' );
146- FOnPaymentCanceled(aData);
147- end
148- else if Assigned(FOnRefundSucceeded) and (aEventType = ' refund.succeeded' ) then
149- begin
150- Log(etInfo, ' Webhook: Triggering OnRefundSucceeded' );
151- FOnRefundSucceeded(aData);
152- end
153- else
154- begin
155- Log(etWarning, Format(' Webhook: Unknown or unhandled event: %s' , [aEventType]));
196+ case aEventType of
197+ ' payment.succeeded' :
198+ if Assigned(FOnPaymentSucceeded) then
199+ begin
200+ Log(etInfo, ' Webhook: Triggering OnPaymentSucceeded' );
201+ FOnPaymentSucceeded(aData);
202+ end ;
203+ ' payment.waiting_for_capture' :
204+ if Assigned(FOnPaymentWaitingForCapture) then
205+ begin
206+ Log(etInfo, ' Webhook: Triggering OnPaymentWaitingForCapture' );
207+ FOnPaymentWaitingForCapture(aData);
208+ end ;
209+ ' payment.canceled' :
210+ if Assigned(FOnPaymentCanceled) then
211+ begin
212+ Log(etInfo, ' Webhook: Triggering OnPaymentCanceled' );
213+ FOnPaymentCanceled(aData);
214+ end ;
215+ ' refund.succeeded' :
216+ if Assigned(FOnRefundSucceeded) then
217+ begin
218+ Log(etInfo, ' Webhook: Triggering OnRefundSucceeded' );
219+ FOnRefundSucceeded(aData);
220+ end ;
221+ { TODO: Add other event handlers
222+ 'payout.succeeded': ...,
223+ 'payout.canceled': ...,
224+ 'deal.closed': ...,
225+ 'payment_method.active': ...
226+ }
227+ else
228+ Log(etWarning, Format(' Webhook: Unknown or unhandled event: %s' , [aEventType]));
156229 end ;
157230
158231 // 4. Return success response
0 commit comments