Skip to content

Commit 7b6940b

Browse files
committed
refactor webhook classes
1 parent 24de94b commit 7b6940b

File tree

2 files changed

+118
-44
lines changed

2 files changed

+118
-44
lines changed

tests/test_yookassa_webhook.pas

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ TTestYookassaWebhook = class(TTestCase)
3838
implementation
3939

4040
uses
41-
StrUtils
41+
StrUtils, yookassa_api
4242
;
4343

4444
{ TTestYookassaWebhook }
@@ -105,9 +105,9 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentSucceeded;
105105
AssertEquals('{"status": "ok"}', aResult);
106106
AssertNotNull('Event should be received', FReceivedEvent);
107107
AssertEquals('payment.succeeded', FReceivedEvent.Event);
108-
AssertEquals('payment', FReceivedEvent.ObjectType);
109-
AssertEquals('pay_123abc', FReceivedEvent.ObjectId);
110-
AssertEquals('succeeded', FReceivedEvent.Status);
108+
AssertEquals(Ord(wotPayment), Ord(FReceivedEvent.ObjectType));
109+
AssertEquals('pay_123abc', FReceivedEvent.PaymentResponse.ID);
110+
AssertEquals(Ord(psSucceeded), Ord(FReceivedEvent.PaymentResponse.Status));
111111
end;
112112

113113
procedure TTestYookassaWebhook.TestHandleWebhook_PaymentWaitingForCapture;
@@ -129,7 +129,7 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentWaitingForCapture;
129129
AssertEquals('{"status": "ok"}', aResult);
130130
AssertNotNull('Event should be received', FReceivedEvent);
131131
AssertEquals('payment.waiting_for_capture', FReceivedEvent.Event);
132-
AssertEquals('pay_456def', FReceivedEvent.ObjectId);
132+
AssertEquals('pay_456def', FReceivedEvent.PaymentResponse.ID);
133133
end;
134134

135135
procedure TTestYookassaWebhook.TestHandleWebhook_PaymentCanceled;
@@ -151,7 +151,7 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentCanceled;
151151
AssertEquals('{"status": "ok"}', aResult);
152152
AssertNotNull('Event should be received', FReceivedEvent);
153153
AssertEquals('payment.canceled', FReceivedEvent.Event);
154-
AssertEquals('canceled', FReceivedEvent.Status);
154+
AssertEquals(Ord(psCanceled), Ord(FReceivedEvent.PaymentResponse.Status));
155155
end;
156156

157157
procedure TTestYookassaWebhook.TestHandleWebhook_RefundSucceeded;
@@ -173,7 +173,8 @@ procedure TTestYookassaWebhook.TestHandleWebhook_RefundSucceeded;
173173
AssertEquals('{"status": "ok"}', aResult);
174174
AssertNotNull('Event should be received', FReceivedEvent);
175175
AssertEquals('refund.succeeded', FReceivedEvent.Event);
176-
AssertEquals('rfnd_999', FReceivedEvent.ObjectId);
176+
{ #todo :
177+
AssertEquals('rfnd_999', FReceivedEvent.RefundResponse.ID); }
177178
end;
178179

179180
procedure TTestYookassaWebhook.TestHandleWebhook_InvalidJSON;

yookassa_webhook.pas

Lines changed: 110 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
interface
66

77
uses
8-
Classes, SysUtils, fpjson;
8+
Classes, SysUtils, fpjson, yookassa_api
9+
;
910

1011
type
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

5991
implementation
6092

93+
uses
94+
StrUtils;
95+
6196
{ EYooKassaWebhookError }
6297

6398
constructor 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+
71135
function TYookassaWebhookData.Clone: TYookassaWebhookData;
72136
begin
73-
Result:=TYookassaWebhookData.Create(Raw.Clone as TJSONObject);
137+
Result := TYookassaWebhookData.Create(Raw.Clone as TJSONObject);
74138
end;
75139

76140
constructor TYookassaWebhookData.Create(aRaw: TJSONObject);
77141
var
78142
aObj: TJSONObject;
79143
begin
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);
86149
end;
87150

88151
destructor TYookassaWebhookData.Destroy;
89152
begin
90-
Raw.Free;
153+
FPaymentResponse.Free;
154+
FRaw.Free;
91155
inherited Destroy;
92156
end;
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

Comments
 (0)