Replace Helcim integration with WooCommerce Stripe
This commit is contained in:
1 parent
197938f482
commit
21038d68ca
23 files changed
+172
-1199
No files matched your search
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Bookstore\Core\Tests\Unit\Payments;
|
||||
|
||||
use Bookstore\Core\Payments\HelcimApiException;
|
||||
use Bookstore\Core\Payments\HelcimClient;
|
||||
use Bookstore\Core\Tests\TestCase;
|
||||
use Brain\Monkey\Functions;
|
||||
|
||||
/**
|
||||
* HelcimGatewayConfig::token() reads getenv('HELCIM_API_TOKEN_FILE') —
|
||||
* mocking that directly is what switches HelcimClient between fake mode
|
||||
* (no token: every call returns a synthetic approved response, no
|
||||
* network call) and real mode (a token: real wp_remote_post calls with
|
||||
* the shape confirmed against Helcim's current developer docs).
|
||||
*/
|
||||
class HelcimClientTest extends TestCase
|
||||
{
|
||||
public function test_fake_mode_never_calls_the_real_api(): void
|
||||
{
|
||||
Functions\when('getenv')->justReturn(false);
|
||||
Functions\when('wp_generate_uuid4')->justReturn('fake-uuid');
|
||||
Functions\expect('wp_remote_post')->never();
|
||||
|
||||
$init = HelcimClient::initialize_checkout(42.00, 'USD', 'preauth');
|
||||
$this->assertTrue($init['fake']);
|
||||
$this->assertNotEmpty($init['checkoutToken']);
|
||||
$this->assertNotEmpty($init['secretToken']);
|
||||
|
||||
$capture = HelcimClient::capture(123456, 42.00, '127.0.0.1');
|
||||
$this->assertSame('APPROVED', $capture['status']);
|
||||
$this->assertIsInt($capture['transactionId']);
|
||||
$this->assertNotEmpty($capture['cardToken']);
|
||||
|
||||
$reverse = HelcimClient::reverse(123456, '127.0.0.1');
|
||||
$this->assertSame('APPROVED', $reverse['status']);
|
||||
}
|
||||
|
||||
public function test_real_mode_initialize_sends_the_documented_request_shape(): void
|
||||
{
|
||||
Functions\when('getenv')->justReturn('/fake/path/to/token');
|
||||
Functions\when('is_readable')->justReturn(true);
|
||||
Functions\when('file_get_contents')->justReturn('real-token-abc');
|
||||
Functions\when('wp_json_encode')->alias('json_encode');
|
||||
Functions\when('is_wp_error')->justReturn(false);
|
||||
Functions\when('wp_remote_retrieve_response_code')->justReturn(200);
|
||||
Functions\when('wp_remote_retrieve_body')->justReturn(json_encode([
|
||||
'checkoutToken' => 'real-checkout-token',
|
||||
'secretToken' => 'real-secret-token',
|
||||
]));
|
||||
|
||||
Functions\expect('wp_remote_post')
|
||||
->once()
|
||||
->withArgs(function ($url, $args) {
|
||||
$this->assertSame('https://api.helcim.com/v2/helcim-pay/initialize', $url);
|
||||
$this->assertSame('real-token-abc', $args['headers']['api-token']);
|
||||
$this->assertArrayNotHasKey('idempotency-key', $args['headers']);
|
||||
$body = json_decode($args['body'], true);
|
||||
$this->assertSame('preauth', $body['paymentType']);
|
||||
$this->assertEquals(42.0, $body['amount']);
|
||||
$this->assertSame('USD', $body['currency']);
|
||||
return true;
|
||||
})
|
||||
->andReturn(['response' => ['code' => 200]]);
|
||||
|
||||
$result = HelcimClient::initialize_checkout(42.00, 'USD', 'preauth');
|
||||
$this->assertSame('real-checkout-token', $result['checkoutToken']);
|
||||
}
|
||||
|
||||
public function test_real_mode_capture_includes_an_idempotency_key(): void
|
||||
{
|
||||
Functions\when('getenv')->justReturn('/fake/path/to/token');
|
||||
Functions\when('is_readable')->justReturn(true);
|
||||
Functions\when('file_get_contents')->justReturn('real-token-abc');
|
||||
Functions\when('wp_json_encode')->alias('json_encode');
|
||||
Functions\when('wp_generate_uuid4')->justReturn('idem-key-123');
|
||||
Functions\when('is_wp_error')->justReturn(false);
|
||||
Functions\when('wp_remote_retrieve_response_code')->justReturn(200);
|
||||
Functions\when('wp_remote_retrieve_body')->justReturn(json_encode(['transactionId' => 999, 'status' => 'APPROVED']));
|
||||
|
||||
Functions\expect('wp_remote_post')
|
||||
->once()
|
||||
->withArgs(function ($url, $args) {
|
||||
$this->assertSame('https://api.helcim.com/v2/payment/capture', $url);
|
||||
$this->assertSame('idem-key-123', $args['headers']['idempotency-key']);
|
||||
$body = json_decode($args['body'], true);
|
||||
$this->assertSame(555, $body['preAuthTransactionId']);
|
||||
$this->assertEquals(42.0, $body['amount']);
|
||||
$this->assertSame('127.0.0.1', $body['ipAddress']);
|
||||
return true;
|
||||
})
|
||||
->andReturn(['response' => ['code' => 200]]);
|
||||
|
||||
$result = HelcimClient::capture(555, 42.00, '127.0.0.1');
|
||||
$this->assertSame(999, $result['transactionId']);
|
||||
}
|
||||
|
||||
public function test_real_mode_throws_on_an_error_response(): void
|
||||
{
|
||||
Functions\when('getenv')->justReturn('/fake/path/to/token');
|
||||
Functions\when('is_readable')->justReturn(true);
|
||||
Functions\when('file_get_contents')->justReturn('real-token-abc');
|
||||
Functions\when('wp_json_encode')->alias('json_encode');
|
||||
Functions\when('wp_generate_uuid4')->justReturn('idem-key-123');
|
||||
Functions\when('is_wp_error')->justReturn(false);
|
||||
Functions\when('wp_remote_retrieve_response_code')->justReturn(422);
|
||||
Functions\when('wp_remote_retrieve_body')->justReturn(json_encode(['errors' => ['card declined']]));
|
||||
Functions\when('wp_remote_post')->justReturn(['response' => ['code' => 422]]);
|
||||
|
||||
$this->expectException(HelcimApiException::class);
|
||||
$this->expectExceptionMessageMatches('/card declined/');
|
||||
|
||||
HelcimClient::capture(555, 42.00, '127.0.0.1');
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Bookstore\Core\Tests\Unit\Payments;
|
||||
|
||||
use Bookstore\Core\Order\OrderStatuses;
|
||||
use Bookstore\Core\Payments\HelcimGateway;
|
||||
use Bookstore\Core\Tests\TestCase;
|
||||
use Brain\Monkey\Functions;
|
||||
use Mockery;
|
||||
|
||||
/**
|
||||
* process_payment()'s branching: pick up the preauth CheckoutController
|
||||
* already stashed (Payments\CheckoutSession, backed by get/set/delete_
|
||||
* transient — mocked at that WP-function layer, not by mocking
|
||||
* CheckoutSession itself, matching this suite's established pattern),
|
||||
* run the (moved-here) fraud check, and either authorize the order or
|
||||
* reverse the hold and route to review. HelcimClient runs in fake mode
|
||||
* here (no token env) so no real network call is involved either way.
|
||||
*/
|
||||
class HelcimGatewayTest extends TestCase
|
||||
{
|
||||
private function stub_pending_preauth(?array $data): void
|
||||
{
|
||||
Functions\when('get_transient')->justReturn($data);
|
||||
Functions\when('delete_transient')->justReturn(true);
|
||||
Functions\when('getenv')->justReturn(false); // HelcimClient stays in fake mode
|
||||
Functions\when('wp_generate_uuid4')->justReturn('fake-uuid');
|
||||
Functions\when('WC')->justReturn((object) ['session' => null]); // CheckoutSession::identity() falls back to session_id()
|
||||
}
|
||||
|
||||
private function mock_order(): \WC_Order
|
||||
{
|
||||
$order = Mockery::mock(\WC_Order::class);
|
||||
$order->shouldReceive('update_meta_data')->withAnyArgs();
|
||||
$order->shouldReceive('save');
|
||||
$order->shouldReceive('add_order_note')->withAnyArgs();
|
||||
$order->shouldReceive('get_meta')->withAnyArgs()->andReturn('');
|
||||
return $order;
|
||||
}
|
||||
|
||||
public function test_no_pending_preauth_fails_without_touching_the_order(): void
|
||||
{
|
||||
$this->stub_pending_preauth(null);
|
||||
Functions\when('wc_get_order')->justReturn(Mockery::mock(\WC_Order::class));
|
||||
Functions\expect('wc_add_notice')->once();
|
||||
|
||||
$gateway = new HelcimGateway();
|
||||
$result = $gateway->process_payment(123);
|
||||
|
||||
$this->assertSame('failure', $result['result']);
|
||||
}
|
||||
|
||||
public function test_fraud_pass_authorizes_the_order(): void
|
||||
{
|
||||
$this->stub_pending_preauth(['preauth_transaction_id' => 555, 'card_token' => 'tok_1', 'amount' => 42.00]);
|
||||
Functions\when('apply_filters')->justReturn(['pass' => true, 'reason' => 'no-op: fraud plugin not installed']);
|
||||
|
||||
$order = $this->mock_order();
|
||||
$order->shouldReceive('update_status')->once()->with(OrderStatuses::AUTHORIZED, Mockery::type('string'));
|
||||
Functions\when('wc_get_order')->justReturn($order);
|
||||
|
||||
$gateway = new HelcimGateway();
|
||||
$result = $gateway->process_payment(123);
|
||||
|
||||
$this->assertSame('success', $result['result']);
|
||||
$this->assertArrayHasKey('redirect', $result);
|
||||
}
|
||||
|
||||
public function test_fraud_fail_reverses_the_hold_and_routes_to_exception(): void
|
||||
{
|
||||
$this->stub_pending_preauth(['preauth_transaction_id' => 555, 'card_token' => 'tok_1', 'amount' => 42.00]);
|
||||
Functions\when('apply_filters')->justReturn(['pass' => false, 'reason' => 'high risk score']);
|
||||
Functions\expect('wc_add_notice')->once();
|
||||
|
||||
$order = $this->mock_order();
|
||||
$order->shouldReceive('update_status')->once()->with(OrderStatuses::EXCEPTION, Mockery::type('string'));
|
||||
Functions\when('wc_get_order')->justReturn($order);
|
||||
|
||||
$gateway = new HelcimGateway();
|
||||
$result = $gateway->process_payment(123);
|
||||
|
||||
$this->assertSame('failure', $result['result']);
|
||||
}
|
||||
}
|
||||
@@ -27,46 +27,6 @@ if (!class_exists(\WC_Cart::class)) {
|
||||
}
|
||||
}
|
||||
|
||||
// Payments\HelcimGateway extends this. Minimal stand-in for the real
|
||||
// WC_Payment_Gateway's settings-API plumbing (init_settings()/
|
||||
// get_option()) so the constructor doesn't fatal in a unit test — real
|
||||
// WooCommerce's version does much more (admin fields rendering, etc.),
|
||||
// none of which the gateway's process_payment() branching depends on.
|
||||
if (!class_exists(\WC_Payment_Gateway::class)) {
|
||||
class WC_Payment_Gateway
|
||||
{
|
||||
public $id;
|
||||
public $method_title;
|
||||
public $method_description;
|
||||
public $has_fields;
|
||||
public $supports = [];
|
||||
public $title;
|
||||
public $description;
|
||||
public $enabled;
|
||||
public $form_fields = [];
|
||||
public $settings = [];
|
||||
|
||||
public function init_settings(): void
|
||||
{
|
||||
$this->settings = [];
|
||||
}
|
||||
|
||||
public function get_option($key, $default = '')
|
||||
{
|
||||
return $this->settings[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function get_return_url($order)
|
||||
{
|
||||
return 'https://example.test/order-received/';
|
||||
}
|
||||
|
||||
public function process_admin_options(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!class_exists(\WP_Error::class)) {
|
||||
class WP_Error
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user