PaypalWebhookServiceTest.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Unit\Payments;
  3. use Mockery;
  4. use Tests\TestCase;
  5. use Webkul\BagistoApi\Models\PaymentAttempt;
  6. use Webkul\BagistoApi\Repositories\PaymentAttemptRepository;
  7. use Webkul\BagistoApi\Services\PaymentService;
  8. use Webkul\Paypal\Services\WebhookService;
  9. use Webkul\Sales\Repositories\OrderRepository;
  10. class PaypalWebhookServiceTest extends TestCase
  11. {
  12. protected function tearDown(): void
  13. {
  14. Mockery::close();
  15. parent::tearDown();
  16. }
  17. private function event(array $overrides = []): array
  18. {
  19. return array_replace_recursive([
  20. 'id' => 'WH-EVENT-1',
  21. 'event_type' => 'PAYMENT.CAPTURE.COMPLETED',
  22. 'resource' => [
  23. 'id' => 'CAPTURE-1',
  24. 'status' => 'COMPLETED',
  25. 'amount' => [
  26. 'value' => '100.00',
  27. 'currency_code' => 'USD',
  28. ],
  29. 'supplementary_data' => [
  30. 'related_ids' => [
  31. 'order_id' => 'PAYPAL-ORDER-1',
  32. ],
  33. ],
  34. ],
  35. ], $overrides);
  36. }
  37. public function test_it_resolves_and_settles_a_completed_capture(): void
  38. {
  39. $attempt = new PaymentAttempt([
  40. 'order_id' => 10,
  41. 'payment_method' => 'paypal_smart_button',
  42. ]);
  43. $order = (object) [
  44. 'id' => 10,
  45. 'payment' => (object) [
  46. 'method' => 'paypal_smart_button',
  47. 'additional' => ['paypal_order_id' => 'PAYPAL-ORDER-1'],
  48. ],
  49. ];
  50. $attempts = Mockery::mock(PaymentAttemptRepository::class);
  51. $attempts->shouldReceive('findByGatewayOrderId')
  52. ->once()
  53. ->with('PAYPAL-ORDER-1')
  54. ->andReturn($attempt);
  55. $orders = Mockery::mock(OrderRepository::class);
  56. $orders->shouldReceive('find')->once()->with(10)->andReturn($order);
  57. $paymentService = Mockery::mock(PaymentService::class);
  58. $paymentService->shouldReceive('completeWebhookCapture')
  59. ->once()
  60. ->withArgs(function ($actualOrder, $gatewayOrderId, array $capture, $eventId) use ($order) {
  61. return $actualOrder === $order
  62. && $gatewayOrderId === 'PAYPAL-ORDER-1'
  63. && $capture['transaction_id'] === 'CAPTURE-1'
  64. && $capture['capture_status'] === 'COMPLETED'
  65. && $capture['amount'] === 100.0
  66. && $capture['currency'] === 'USD'
  67. && $eventId === 'WH-EVENT-1';
  68. })
  69. ->andReturn(['gatewayStatus' => 'captured', 'order' => $order]);
  70. $service = new WebhookService($attempts, $orders, $paymentService);
  71. $result = $service->handleCaptureCompleted($this->event());
  72. $this->assertSame('captured', $result['gatewayStatus']);
  73. }
  74. public function test_it_rejects_an_event_without_a_related_paypal_order(): void
  75. {
  76. $service = new WebhookService(
  77. Mockery::mock(PaymentAttemptRepository::class),
  78. Mockery::mock(OrderRepository::class),
  79. Mockery::mock(PaymentService::class),
  80. );
  81. $event = $this->event();
  82. unset($event['resource']['supplementary_data']);
  83. $this->expectException(\InvalidArgumentException::class);
  84. $this->expectExceptionMessage('PayPal order id is missing');
  85. $service->handleCaptureCompleted($event);
  86. }
  87. public function test_it_rejects_a_gateway_order_mismatch(): void
  88. {
  89. $attempt = new PaymentAttempt([
  90. 'order_id' => 10,
  91. 'payment_method' => 'paypal_smart_button',
  92. ]);
  93. $order = (object) [
  94. 'id' => 10,
  95. 'payment' => (object) [
  96. 'method' => 'paypal_smart_button',
  97. 'additional' => ['paypal_order_id' => 'ANOTHER-ORDER'],
  98. ],
  99. ];
  100. $attempts = Mockery::mock(PaymentAttemptRepository::class);
  101. $attempts->shouldReceive('findByGatewayOrderId')->andReturn($attempt);
  102. $orders = Mockery::mock(OrderRepository::class);
  103. $orders->shouldReceive('find')->andReturn($order);
  104. $service = new WebhookService(
  105. $attempts,
  106. $orders,
  107. Mockery::mock(PaymentService::class),
  108. );
  109. $this->expectException(\InvalidArgumentException::class);
  110. $this->expectExceptionMessage('does not match');
  111. $service->handleCaptureCompleted($this->event());
  112. }
  113. public function test_it_rejects_a_non_completed_capture(): void
  114. {
  115. $attempt = new PaymentAttempt([
  116. 'order_id' => 10,
  117. 'payment_method' => 'paypal_smart_button',
  118. ]);
  119. $order = (object) [
  120. 'id' => 10,
  121. 'payment' => (object) [
  122. 'method' => 'paypal_smart_button',
  123. 'additional' => ['paypal_order_id' => 'PAYPAL-ORDER-1'],
  124. ],
  125. ];
  126. $attempts = Mockery::mock(PaymentAttemptRepository::class);
  127. $attempts->shouldReceive('findByGatewayOrderId')->andReturn($attempt);
  128. $orders = Mockery::mock(OrderRepository::class);
  129. $orders->shouldReceive('find')->andReturn($order);
  130. $service = new WebhookService(
  131. $attempts,
  132. $orders,
  133. Mockery::mock(PaymentService::class),
  134. );
  135. $this->expectException(\InvalidArgumentException::class);
  136. $this->expectExceptionMessage('not completed');
  137. $service->handleCaptureCompleted($this->event([
  138. 'resource' => ['status' => 'PENDING'],
  139. ]));
  140. }
  141. }