@@ -33,12 +33,22 @@ TTestYookassaWebhook = class(TTestCase)
3333 procedure TestHandleWebhook_InvalidJSON ;
3434 procedure TestHandleWebhook_UnknownEvent ;
3535 procedure TestHandleWebhook_Logging ;
36+ // New tests for PaymentResponse property and object type detection
37+ procedure TestWebhookData_PaymentResponse_Properties ;
38+ procedure TestWebhookData_ObjectType_Payment ;
39+ procedure TestWebhookData_ObjectType_Refund ;
40+ procedure TestWebhookData_ObjectType_Unknown ;
41+ procedure TestWebhookData_Clone ;
42+ procedure TestWebhookData_Event_Property ;
43+ procedure TestWebhookData_PaymentResponse_Creation ;
44+ procedure TestWebhookData_PaymentResponse_Null_When_No_Object ;
45+ procedure TestWebhookException_Creation ;
3646 end ;
3747
3848implementation
3949
4050uses
41- StrUtils, yookassa_api
51+ StrUtils, yookassa_api, fpjson
4252 ;
4353
4454{ TTestYookassaWebhook }
@@ -93,7 +103,9 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentSucceeded;
93103 ' "object":{' +
94104 ' "id":"pay_123abc",' +
95105 ' "type":"payment",' +
96- ' "status":"succeeded"' +
106+ ' "status":"succeeded",' +
107+ ' "amount":{"value":"100.00","currency":"RUB"},' +
108+ ' "confirmation":{"confirmation_url":"https://yookassa.ru/checkout/pay/test"}' +
97109 ' }' +
98110 ' }' ;
99111var
@@ -117,7 +129,8 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentWaitingForCapture;
117129 ' "object":{' +
118130 ' "id":"pay_456def",' +
119131 ' "type":"payment",' +
120- ' "status":"waiting_for_capture"' +
132+ ' "status":"waiting_for_capture",' +
133+ ' "amount":{"value":"200.00","currency":"RUB"}' +
121134 ' }' +
122135 ' }' ;
123136var
@@ -130,6 +143,7 @@ procedure TTestYookassaWebhook.TestHandleWebhook_PaymentWaitingForCapture;
130143 AssertNotNull(' Event should be received' , FReceivedEvent);
131144 AssertEquals(' payment.waiting_for_capture' , FReceivedEvent.Event);
132145 AssertEquals(' pay_456def' , FReceivedEvent.PaymentResponse.ID);
146+ AssertEquals(Ord(psWaitingForCapture), Ord(FReceivedEvent.PaymentResponse.Status));
133147end ;
134148
135149procedure TTestYookassaWebhook.TestHandleWebhook_PaymentCanceled ;
@@ -173,8 +187,7 @@ procedure TTestYookassaWebhook.TestHandleWebhook_RefundSucceeded;
173187 AssertEquals(' {"status": "ok"}' , aResult);
174188 AssertNotNull(' Event should be received' , FReceivedEvent);
175189 AssertEquals(' refund.succeeded' , FReceivedEvent.Event);
176- { #todo :
177- AssertEquals('rfnd_999', FReceivedEvent.RefundResponse.ID); }
190+ AssertEquals(Ord(wotRefund), Ord(FReceivedEvent.ObjectType));
178191end ;
179192
180193procedure TTestYookassaWebhook.TestHandleWebhook_InvalidJSON ;
@@ -183,7 +196,7 @@ procedure TTestYookassaWebhook.TestHandleWebhook_InvalidJSON;
183196begin
184197 aResult := FHandler.HandleWebhook(' {"invalid": json}' );
185198 AssertTrue(' Response should indicate error' , Pos(' error' , aResult) > 0 );
186- AssertTrue(StartsStr(' Webhook: Invalid JSON - ' , FLogMsg)); // Log not called due to early exit
199+ AssertTrue(' Should log invalid JSON error ' , StartsStr(' Webhook: Invalid JSON - ' , FLogMsg));
187200end ;
188201
189202procedure TTestYookassaWebhook.TestHandleWebhook_UnknownEvent ;
@@ -222,8 +235,223 @@ procedure TTestYookassaWebhook.TestHandleWebhook_Logging;
222235 AssertEquals(Ord(etInfo), Ord(FLogEventType));
223236end ;
224237
238+ procedure TTestYookassaWebhook.TestWebhookData_PaymentResponse_Properties ;
239+ const
240+ RawBody = ' {' +
241+ ' "event":"payment.succeeded",' +
242+ ' "object":{' +
243+ ' "id":"pay_test_props",' +
244+ ' "type":"payment",' +
245+ ' "status":"succeeded",' +
246+ ' "amount":{"value":"150.75","currency":"RUB"},' +
247+ ' "confirmation":{"confirmation_url":"https://yookassa.ru/checkout/pay/props"}' +
248+ ' }' +
249+ ' }' ;
250+ var
251+ aJSON: TJSONObject;
252+ aWebhookData: TYookassaWebhookData;
253+ aPaymentResp: TYookassaPaymentResponse;
254+ begin
255+ aJSON := TJSONObject(GetJSON(RawBody));
256+ aWebhookData := TYookassaWebhookData.Create(aJSON);
257+ try
258+ aPaymentResp := aWebhookData.PaymentResponse;
259+ AssertNotNull(' PaymentResponse should be created' , aPaymentResp);
260+ AssertEquals(' Payment ID should match' , ' pay_test_props' , aPaymentResp.ID);
261+ AssertEquals(' Payment status should be succeeded' , Ord(psSucceeded), Ord(aPaymentResp.Status));
262+ AssertEquals(' Payment amount should match' , 150.75 , aPaymentResp.Amount);
263+ AssertEquals(' Payment currency should match' , ' RUB' , aPaymentResp.Currency);
264+ AssertEquals(' Confirmation URL should match' , ' https://yookassa.ru/checkout/pay/props' , aPaymentResp.ConfirmationURL);
265+ finally
266+ aWebhookData.Free;
267+ end ;
268+ end ;
269+
270+ procedure TTestYookassaWebhook.TestWebhookData_ObjectType_Payment ;
271+ const
272+ RawBody = ' {' +
273+ ' "event":"payment.succeeded",' +
274+ ' "object":{"id":"pay_123","type":"payment"}' +
275+ ' }' ;
276+ var
277+ aJSON: TJSONObject;
278+ aWebhookData: TYookassaWebhookData;
279+ begin
280+ aJSON := TJSONObject(GetJSON(RawBody));
281+ aWebhookData := TYookassaWebhookData.Create(aJSON);
282+ try
283+ AssertEquals(' Object type should be payment' , Ord(wotPayment), Ord(aWebhookData.ObjectType));
284+ AssertEquals(' Event should match' , ' payment.succeeded' , aWebhookData.Event);
285+ finally
286+ aWebhookData.Free;
287+ end ;
288+ end ;
289+
290+ procedure TTestYookassaWebhook.TestWebhookData_ObjectType_Refund ;
291+ const
292+ RawBody = ' {' +
293+ ' "event":"refund.succeeded",' +
294+ ' "object":{"id":"rfnd_123","type":"refund"}' +
295+ ' }' ;
296+ var
297+ aJSON: TJSONObject;
298+ aWebhookData: TYookassaWebhookData;
299+ begin
300+ aJSON := TJSONObject(GetJSON(RawBody));
301+ aWebhookData := TYookassaWebhookData.Create(aJSON);
302+ try
303+ AssertEquals(' Object type should be refund' , Ord(wotRefund), Ord(aWebhookData.ObjectType));
304+ AssertEquals(' Event should match' , ' refund.succeeded' , aWebhookData.Event);
305+ finally
306+ aWebhookData.Free;
307+ end ;
308+ end ;
309+
310+ procedure TTestYookassaWebhook.TestWebhookData_ObjectType_Unknown ;
311+ const
312+ RawBody = ' {' +
313+ ' "event":"custom.event",' +
314+ ' "object":{"id":"obj_123","type":"custom"}' +
315+ ' }' ;
316+ var
317+ aJSON: TJSONObject;
318+ aWebhookData: TYookassaWebhookData;
319+ begin
320+ aJSON := TJSONObject(GetJSON(RawBody));
321+ aWebhookData := TYookassaWebhookData.Create(aJSON);
322+ try
323+ AssertEquals(' Object type should be unknown' , Ord(wotUnknown), Ord(aWebhookData.ObjectType));
324+ AssertEquals(' Event should match' , ' custom.event' , aWebhookData.Event);
325+ finally
326+ aWebhookData.Free;
327+ end ;
328+ end ;
329+
330+ procedure TTestYookassaWebhook.TestWebhookData_Clone ;
331+ const
332+ RawBody = ' {' +
333+ ' "event":"payment.succeeded",' +
334+ ' "object":{' +
335+ ' "id":"pay_clone_test",' +
336+ ' "type":"payment",' +
337+ ' "status":"succeeded"' +
338+ ' }' +
339+ ' }' ;
340+ var
341+ aJSON: TJSONObject;
342+ aOriginal, aClone: TYookassaWebhookData;
343+ begin
344+ aJSON := TJSONObject(GetJSON(RawBody));
345+ aOriginal := TYookassaWebhookData.Create(aJSON);
346+ try
347+ aClone := aOriginal.Clone;
348+ try
349+ AssertNotNull(' Clone should not be nil' , aClone);
350+ AssertTrue(' Clone should be different object' , aOriginal <> aClone);
351+ AssertEquals(' Clone event should match original' , aOriginal.Event, aClone.Event);
352+ AssertEquals(' Clone object type should match original' , Ord(aOriginal.ObjectType), Ord(aClone.ObjectType));
353+
354+ // Test that both can access PaymentResponse independently
355+ AssertNotNull(' Original PaymentResponse should work' , aOriginal.PaymentResponse);
356+ AssertNotNull(' Clone PaymentResponse should work' , aClone.PaymentResponse);
357+ AssertEquals(' Both should have same payment ID' ,
358+ aOriginal.PaymentResponse.ID, aClone.PaymentResponse.ID);
359+ finally
360+ aClone.Free;
361+ end ;
362+ finally
363+ aOriginal.Free;
364+ end ;
365+ end ;
366+
367+ procedure TTestYookassaWebhook.TestWebhookData_Event_Property ;
368+ const
369+ RawBody = ' {' +
370+ ' "event":"payment.waiting_for_capture",' +
371+ ' "object":{"id":"pay_123","type":"payment"}' +
372+ ' }' ;
373+ var
374+ aJSON: TJSONObject;
375+ aWebhookData: TYookassaWebhookData;
376+ begin
377+ aJSON := TJSONObject(GetJSON(RawBody));
378+ aWebhookData := TYookassaWebhookData.Create(aJSON);
379+ try
380+ AssertEquals(' Event property should return correct value' ,
381+ ' payment.waiting_for_capture' , aWebhookData.Event);
382+ finally
383+ aWebhookData.Free;
384+ end ;
385+ end ;
386+
387+ procedure TTestYookassaWebhook.TestWebhookData_PaymentResponse_Creation ;
388+ const
389+ RawBody = ' {' +
390+ ' "event":"payment.succeeded",' +
391+ ' "object":{' +
392+ ' "id":"pay_creation_test",' +
393+ ' "type":"payment",' +
394+ ' "status":"succeeded",' +
395+ ' "amount":{"value":"99.99","currency":"USD"}' +
396+ ' }' +
397+ ' }' ;
398+ var
399+ aJSON: TJSONObject;
400+ aWebhookData: TYookassaWebhookData;
401+ aPaymentResp1, aPaymentResp2: TYookassaPaymentResponse;
402+ begin
403+ aJSON := TJSONObject(GetJSON(RawBody));
404+ aWebhookData := TYookassaWebhookData.Create(aJSON);
405+ try
406+ // Test lazy creation and caching
407+ aPaymentResp1 := aWebhookData.PaymentResponse;
408+ aPaymentResp2 := aWebhookData.PaymentResponse;
409+
410+ AssertNotNull(' First call should create PaymentResponse' , aPaymentResp1);
411+ AssertNotNull(' Second call should return cached PaymentResponse' , aPaymentResp2);
412+ AssertTrue(' Both calls should return same instance' , aPaymentResp1 = aPaymentResp2);
413+
414+ AssertEquals(' Payment ID should be correct' , ' pay_creation_test' , aPaymentResp1.ID);
415+ AssertEquals(' Payment amount should be correct' , 99.99 , aPaymentResp1.Amount);
416+ AssertEquals(' Payment currency should be correct' , ' USD' , aPaymentResp1.Currency);
417+ finally
418+ aWebhookData.Free;
419+ end ;
420+ end ;
421+
422+ procedure TTestYookassaWebhook.TestWebhookData_PaymentResponse_Null_When_No_Object ;
423+ const
424+ RawBody = ' {' +
425+ ' "event":"payment.succeeded"' +
426+ ' }' ;
427+ var
428+ aJSON: TJSONObject;
429+ aWebhookData: TYookassaWebhookData;
430+ begin
431+ aJSON := TJSONObject(GetJSON(RawBody));
432+ aWebhookData := TYookassaWebhookData.Create(aJSON);
433+ try
434+ AssertNull(' PaymentResponse should be nil when no object in webhook' ,
435+ aWebhookData.PaymentResponse);
436+ finally
437+ aWebhookData.Free;
438+ end ;
439+ end ;
440+
441+ procedure TTestYookassaWebhook.TestWebhookException_Creation ;
442+ var
443+ aException: EYooKassaWebhookError;
444+ begin
445+ aException := EYooKassaWebhookError.Create(' Test webhook error' , ' WEBHOOK_ERROR' );
446+ try
447+ AssertEquals(' Message should match' , ' Test webhook error' , aException.Message);
448+ AssertEquals(' Error code should match' , ' WEBHOOK_ERROR' , aException.ErrorCode);
449+ finally
450+ aException.Free;
451+ end ;
452+ end ;
453+
225454initialization
226455 RegisterTest(TTestYookassaWebhook);
227456
228457end .
229-
0 commit comments