Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WooPayments payment gateway #6

Merged
merged 9 commits into from
Nov 14, 2024
119 changes: 0 additions & 119 deletions src/inc/payment-gateways/lib/stripe.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,125 +6,6 @@
* A class to share Stripe-specific logic with payment gateways that use Stripe in some way.
*/
class Stripe {
/**
* Get all payment methods for customer from order.
*
* @param \WC_Order $order The WC Order.
*
* @return array|null
*/
public static function get_all_payment_methods_for_customer_from_order( \WC_Order $order ): ?array {
$stripe_customer_id = $order->get_meta( '_stripe_customer_id', true );

$stripe_customer = new \WC_Stripe_Customer();
$stripe_customer->set_id( $stripe_customer_id );

$sources = array_merge(
$stripe_customer->get_payment_methods( 'card' ),
$stripe_customer->get_payment_methods( 'sepa_debit' )
);

if ( $sources ) {
return $sources;
}
}

/**
* Get payment method from order.
*
* @param \WC_Order $order The WC Order.
*
* @return object The payment method from the order.
*/
public static function get_payment_method_from_order( \WC_Order $order ) {
$stripe_source_id = $order->get_meta( '_stripe_source_id', true );
$sources = static::get_all_payment_methods_for_customer_from_order( $order );

if ( $sources ) {
foreach ( $sources as $source ) {
if ( $source->id === $stripe_source_id ) {
return $source;
}
}
}
}

/**
* Get payment intent from order.
*
* @param \WC_Order $order The WC Order.
*
* @return object The payment intent from the order.
*/
public static function get_intent_from_order( \WC_Order $order ) {
$intent_id = $order->get_meta( '_stripe_intent_id' );

if ( $intent_id ) {
return static::get_intent( 'payment_intents', $intent_id );
}

// The order doesn't have a payment intent, but it may have a setup intent.
$intent_id = $order->get_meta( '_stripe_setup_intent' );

if ( $intent_id ) {
return static::get_intent( 'setup_intents', $intent_id );
}

return false;
}

/**
* Get the intent by ID from the Stripe API.
*
* @param string $intent_type The intent type.
* @param string $intent_id The intent's ID.
*
* @return object The intent from the API.
*/
public static function get_intent( string $intent_type, string $intent_id ) {
if ( ! in_array( $intent_type, array( 'payment_intents', 'setup_intents' ), true ) ) {
throw new \Exception( sprintf( 'Failed to get intent of type %s. Type is not allowed', esc_attr( $intent_type ) ) );
}

$response = \WC_Stripe_API::request( array(), "$intent_type/$intent_id?expand[]=payment_method", 'GET' );

if ( $response && isset( $response->{ 'error' } ) ) {
return false;
}

return $response;
}

/**
* Get the charge for a payment intent from a WC_Order object.
*
* @param \WC_Order $order The WC Order.
*
* @return object The charge for the intent from the order.
*/
public static function get_charge_for_intent_from_order( \WC_Order $order ) {
$intent = self::get_intent_from_order( $order );
if ( ! empty( $intent ) ) {
$result = \WC_Stripe_API::request(
array(),
'payment_intents/' . $intent->id
);
if ( empty( $result->error ) ) {
return end( $result->charges->data );
}
}
}

/**
* Get the Stripe UPE payment type from Order metadata.
*
* @param \WC_Order $order The WC Order.
*
* @return null|string
*/
public static function get_payment_type_from_order( \WC_Order $order ): ?string {
return $order->get_meta( '_stripe_upe_payment_type' );
}

/**
* Convert a payment method string from a string Stripe would use to a string Sift would use.
Expand Down
4 changes: 4 additions & 0 deletions src/inc/payment-gateways/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ function () {
case 'woopayments':
require_once __DIR__ . '/transact.php';
break;
case 'woocommerce_payments':
require_once __DIR__ . '/lib/stripe.php';
require_once __DIR__ . '/woocommerce-payments.php';
break;
}
}
}
Expand Down
132 changes: 130 additions & 2 deletions src/inc/payment-gateways/stripe.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,148 @@
<?php declare( strict_types=1 );

namespace Sift_For_WooCommerce\Sift_For_WooCommerce\PaymentGateways;

use Sift_For_WooCommerce\Sift_For_WooCommerce\PaymentGateways\Lib\Stripe;

/**
* Utility functions used by multiple filters when working with the woocommerce-gateway-stripe plugin.
*/
class WooCommerce_Gateway_Stripe_Utils {

/**
* Get all payment methods for customer from order.
*
* @param \WC_Order $order The WC Order.
*
* @return array|null
*/
public static function get_all_payment_methods_for_customer_from_order( \WC_Order $order ): ?array {
$stripe_customer_id = $order->get_meta( '_stripe_customer_id', true );

$stripe_customer = new \WC_Stripe_Customer();
$stripe_customer->set_id( $stripe_customer_id );

$sources = array_merge(
$stripe_customer->get_payment_methods( 'card' ),
$stripe_customer->get_payment_methods( 'sepa_debit' )
);

if ( $sources ) {
return $sources;
}
}

/**
* Get payment method from order.
*
* @param \WC_Order $order The WC Order.
*
* @return object The payment method from the order.
*/
public static function get_payment_method_from_order( \WC_Order $order ) {
$stripe_source_id = $order->get_meta( '_stripe_source_id', true );
$sources = static::get_all_payment_methods_for_customer_from_order( $order );

if ( $sources ) {
foreach ( $sources as $source ) {
if ( $source->id === $stripe_source_id ) {
return $source;
}
}
}
}

/**
* Get payment intent from order.
*
* @param \WC_Order $order The WC Order.
*
* @return object The payment intent from the order.
*/
public static function get_intent_from_order( \WC_Order $order ) {
$intent_id = $order->get_meta( '_stripe_intent_id' );

if ( $intent_id ) {
return static::get_intent( 'payment_intents', $intent_id );
}

// The order doesn't have a payment intent, but it may have a setup intent.
$intent_id = $order->get_meta( '_stripe_setup_intent' );

if ( $intent_id ) {
return static::get_intent( 'setup_intents', $intent_id );
}

return false;
}

/**
* Get the intent by ID from the Stripe API.
*
* @param string $intent_type The intent type.
* @param string $intent_id The intent's ID.
*
* @return object The intent from the API.
*/
public static function get_intent( string $intent_type, string $intent_id ) {
if ( ! in_array( $intent_type, array( 'payment_intents', 'setup_intents' ), true ) ) {
throw new \Exception( sprintf( 'Failed to get intent of type %s. Type is not allowed', esc_attr( $intent_type ) ) );
}

$response = \WC_Stripe_API::request( array(), "$intent_type/$intent_id?expand[]=payment_method", 'GET' );

if ( $response && isset( $response->{ 'error' } ) ) {
return false;
}

return $response;
}

/**
* Get the charge for a payment intent from a WC_Order object.
*
* @param \WC_Order $order The WC Order.
*
* @return object The charge for the intent from the order.
*/
public static function get_charge_for_intent_from_order( \WC_Order $order ) {
$intent = self::get_intent_from_order( $order );
if ( ! empty( $intent ) ) {
$result = \WC_Stripe_API::request(
array(),
'payment_intents/' . $intent->id
);
if ( empty( $result->error ) ) {
return end( $result->charges->data );
}
}
}

/**
* Get the Stripe UPE payment type from Order metadata.
*
* @param \WC_Order $order The WC Order.
*
* @return null|string
*/
public static function get_payment_type_from_order( \WC_Order $order ): ?string {
return $order->get_meta( '_stripe_upe_payment_type' );
}
}

add_filter( 'sift_for_woocommerce_stripe_payment_gateway_string', fn() => '$stripe' );

add_filter(
'sift_for_woocommerce_stripe_payment_method_details_from_order',
function ( \WC_Order $order ) {
return Stripe::get_payment_method_from_order( $order );
return WooCommerce_Gateway_Stripe_Utils::get_payment_method_from_order( $order );
}
);

add_filter(
'sift_for_woocommerce_stripe_charge_details_from_order',
function ( \WC_Order $order ) {
return Stripe::get_charge_for_intent_from_order( $order );
return WooCommerce_Gateway_Stripe_Utils::get_charge_for_intent_from_order( $order );
}
);

Expand Down
10 changes: 1 addition & 9 deletions src/inc/payment-gateways/transact.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,7 @@

add_filter( 'sift_for_woocommerce_woopayments_payment_gateway_string', fn() => '$stripe' );

add_filter(
'sift_for_woocommerce_woopayments_payment_type_string',
function ( $payment_type ) {
switch ( strtolower( $payment_type ) ) {
case 'card':
return '$credit_card';
}
}
);
add_filter( 'sift_for_woocommerce_woopayments_payment_type_string', array( \Sift_For_WooCommerce\Sift_For_WooCommerce\PaymentGateways\Lib\Stripe::class, 'convert_payment_type_to_sift_payment_type' ) );

add_filter( 'sift_for_woocommerce_woopayments_payment_method_details_from_order', fn( $value, $order ) => $order ?? $value, 10, 2 );

Expand Down
44 changes: 44 additions & 0 deletions src/inc/payment-gateways/woocommerce-payments.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

add_filter( 'sift_for_woocommerce_woocommerce_payments_payment_gateway_string', fn() => '$stripe' );

add_filter(
'sift_for_woocommerce_woocommerce_payments_payment_method_details_from_order',
function ( $value, \WC_Order $order ) {
try {
$charge_id = \WC_Payments::get_order_service()->get_charge_id_for_order( $order );
$api_client = \WC_Payments::get_payments_api_client();
$charge = $api_client->get_charge( $charge_id );
return $charge->get_payment_method_details();
} catch ( \Exception ) {
return $value;
}
}
);

add_filter(
'sift_for_woocommerce_woocommerce_payments_charge_details_from_order',
function ( $value, \WC_Order $order ) {
try {
$charge_id = \WC_Payments::get_order_service()->get_charge_id_for_order( $order );
$api_client = \WC_Payments::get_payments_api_client();
return $api_client->get_charge( $charge_id );
} catch ( \Exception ) {
return $value;
}
}
);

add_filter( 'sift_for_woocommerce_woocommerce_payments_payment_type_string', array( \Sift_For_WooCommerce\Sift_For_WooCommerce\PaymentGateways\Lib\Stripe::class, 'convert_payment_type_to_sift_payment_type' ) );
add_filter( 'sift_for_woocommerce_woocommerce_payments_card_last4', fn( $value, $woocommerce_payments_payment_method_details ) => $woocommerce_payments_payment_method_details['card']['last4'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_card_bin', fn( $value, $woocommerce_payments_payment_method_details ) => $woocommerce_payments_payment_method_details['card']['iin'] ?? $value, 10, 2 );

add_filter( 'sift_for_woocommerce_woocommerce_payments_cvv_result_code', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['checks']['cvc_check'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_sepa_direct_debit_mandate', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['sepa_debit']['mandate'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_wallet_type', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['wallet']['type'] ?? $value, 10, 2 );

add_filter( 'sift_for_woocommerce_woocommerce_payments_stripe_cvc_check', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['checks']['cvc_check'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_stripe_address_line1_check', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['checks']['address_line1_check'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_stripe_address_zip_check', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['checks']['address_postal_code_check'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_stripe_funding', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['funding'] ?? $value, 10, 2 );
add_filter( 'sift_for_woocommerce_woocommerce_payments_stripe_brand', fn( $value, $woocommerce_payments_charge ) => $woocommerce_payments_charge->get_payment_method_details()['card']['brand'] ?? $value, 10, 2 );
10 changes: 8 additions & 2 deletions tests/PaymentGatewayTest.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?php
/**
* Class PaymentGatewayTest
*
* @package Sift_Decisions
*/

use Sift_For_WooCommerce\Sift_For_WooCommerce\Payment_Gateway;

require_once __DIR__ . '/../src/inc/payment-gateway.php';
require_once __DIR__ . '/../src/inc/payment-gateways/index.php';
require_once __DIR__ . '/../src/inc/payment-gateways/lib/stripe.php';
require_once __DIR__ . '/../src/inc/payment-gateways/stripe.php';
require_once __DIR__ . '/../src/inc/payment-gateways/transact.php';
require_once __DIR__ . '/../src/inc/payment-gateways/woocommerce-payments.php';

/**
* Tests for payment gateway interoperability
Expand Down Expand Up @@ -55,6 +55,12 @@ public function payment_gateway_provider(): array {
'is_valid' => true,
'sift_slug' => '$stripe',
),
'WooCommerce Payments is a valid payment gateway' => array(
'woo_gateway_id' => 'woocommerce_payments',
'expected_woo_gateway_id' => 'woocommerce_payments',
'is_valid' => true,
'sift_slug' => '$stripe',
),
'WooPayments is a valid payment gateway' => array(
'woo_gateway_id' => 'woopayments',
'expected_woo_gateway_id' => 'woopayments',
Expand Down
Loading
Loading