diff --git a/src/Gateways/AbstractGateway.php b/src/Gateways/AbstractGateway.php index 9292e50..62902aa 100644 --- a/src/Gateways/AbstractGateway.php +++ b/src/Gateways/AbstractGateway.php @@ -71,6 +71,9 @@ abstract class AbstractGateway extends WC_Payment_Gateway_Cc { const TXN_TYPE_INITIATE_AUTHENTICATION = 'initiateAuthentication'; const TXN_TYPE_PAYPAL_INITIATE = 'initiatePayment'; + // Subscription + const TXN_TYPE_SUBSCRIPTION_PAYMENT = 'subscriptionPayment'; + /** * Gateway provider. Should be overriden by individual gateway implementations @@ -956,6 +959,11 @@ public function process_payment( $order_id ) { $this->payment_action, $order->get_transaction_id() ); + // If the order contains a subscription, add the muti-use token to the order meta for repeat payments. + if ( function_exists( "wcs_order_contains_subscription" ) && wcs_order_contains_subscription( $order ) && $is_successful ) { + $order->add_meta_data( "_GP_multi_use_token", $response->token, true ); + $order->save_meta_data(); + } $order->add_order_note( $note_text ); } @@ -1167,6 +1175,7 @@ public function prepare_request( $txn_type, WC_Order $order = null, $configData self::TXN_TYPE_BNPL_AUTHORIZE => Requests\BuyNowPayLater\InitiatePaymentRequest::class, self::TXN_TYPE_OB_AUTHORIZATION => Requests\OpenBanking\InitiatePaymentRequest::class, self::TXN_TYPE_PAYPAL_INITIATE => Requests\Apm\InitiatePaymentRequest::class, + self::TXN_TYPE_SUBSCRIPTION_PAYMENT => Requests\Subscriptions\SubscriptionRequest::class ); if ( ! isset( $map[ $txn_type ] ) ) { diff --git a/src/Gateways/Clients/SdkClient.php b/src/Gateways/Clients/SdkClient.php index 017bd9f..ccfca79 100644 --- a/src/Gateways/Clients/SdkClient.php +++ b/src/Gateways/Clients/SdkClient.php @@ -237,6 +237,12 @@ protected function prepare_request_objects() { $this->builder_args['requestMultiUseToken'] = array( true ); } } + // Checks if order contains a subscription and requests a muti-use token. + if ( function_exists( "wcs_order_contains_subscription" ) ) { + if ( isset( $this->builder_args['orderId'] ) && wcs_order_contains_subscription( wc_get_order( $this->builder_args['orderId'][0] ) ) ) { + $this->builder_args['requestMultiUseToken'] = array( true ); + } + } if ( $this->has_arg( RequestArg::DIGITAL_WALLET_TOKEN ) ) { $this->card_data = new CreditCardData(); diff --git a/src/Gateways/GpApiGateway.php b/src/Gateways/GpApiGateway.php index a075c8e..0ab2484 100644 --- a/src/Gateways/GpApiGateway.php +++ b/src/Gateways/GpApiGateway.php @@ -376,6 +376,25 @@ protected function add_hooks() { 'process_threeDSecure_challengeNotification' ) ); add_action( 'woocommerce_order_actions', array( $this, 'handle_adding_capture_order_action' ) ); + + add_action( 'woocommerce_scheduled_subscription_payment_' . $this->id, array( + $this, + 'renew_subscription' + ), 10, 3 ); + // When subscription is canceled remove the meta data + add_action( 'woocommerce_subscription_status_pending-cancel_to_cancelled', function( $subscription ) { + if ( $subscription->get_payment_method() === $this->id ) { + $original_order = wc_get_order( $subscription->get_parent_id() ); + if( $original_order->get_meta( "_GP_multi_use_token" ) ) { + $original_order->delete_meta_data( '_GP_multi_use_token' ); + $original_order->save(); + } else { + return; + } + } else { + return; + } + } ); } public function woocommerce_globalpayments_gpapi_settings( $settings ) { @@ -550,6 +569,42 @@ public function handle_adding_capture_order_action( $actions ) { return $actions; } + /** + * Handles subscription renewals, gets the muti-use token from the order meta and charges it. + * @return bool + */ + public function renew_subscription( $amount_to_charge, $renewal_order ) { + $renewal_order_subscriptions = wcs_get_subscriptions_for_renewal_order( $renewal_order->get_id(),array( "order_type"=>"parent") ); + $parent_order = false; + foreach ( $renewal_order_subscriptions as $renewal_order_subscription ) { + if ( $renewal_order_subscription->get_parent_id() ) { + $parent_order = wc_get_order( $renewal_order_subscription->get_parent_id() ); + } + } + + if ( !$parent_order || !$parent_order->get_meta( "_GP_multi_use_token" ) ) { + + return; + } + $request = $this->prepare_request( parent::TXN_TYPE_SUBSCRIPTION_PAYMENT, $renewal_order, array( "multi_use_token"=> $parent_order->get_meta( "_GP_multi_use_token" ) ) ); + $response = $this->client->submit_request( $request ); + $client_trans_ref = $response->transactionReference->clientTransactionId; + + if ( parent::handle_response( $request, $response ) ) { + $renewal_order->add_order_note( sprintf( __( "Subscription Renewal Successful \r\n Transaction Reference: %s", "globalpayments-gateway-provider-for-woocommerce" ), $client_trans_ref ) ); + $renewal_order->payment_complete(); + $renewal_order->save(); + + return true; + } else { + $renewal_order->add_order_note( sprintf( __( "Subscription Renewal Payment Failed \r\nTransaction Reference: %s", "globalpayments-gateway-provider-for-woocommerce" ), $client_trans_ref ) ); + $renewal_order->save(); + + return false; + } + + return false; + } /** * Returns gateway supported payment methods. * diff --git a/src/Gateways/Requests/Subscriptions/SubscriptionRequest.php b/src/Gateways/Requests/Subscriptions/SubscriptionRequest.php new file mode 100644 index 0000000..a855460 --- /dev/null +++ b/src/Gateways/Requests/Subscriptions/SubscriptionRequest.php @@ -0,0 +1,79 @@ +get_muti_use_token(); + $tokenizedCard = new CreditCardData(); + $tokenizedCard->token = $muti_use_token; + $customer_details = $this->get_customer_data(); + $tokenizedCard->cardHolderName = $customer_details->firstName . " " . $customer_details->lastName; + $response = $tokenizedCard->charge( $this->order->get_total() ) + ->withCurrency( $this->order->get_currency() ) + ->withOrderId( (string) $this->order->get_id() ) + ->withAddress( $this->get_shipping_details(), AddressType::SHIPPING ) + ->withAddress( $this->get_billing_address(), AddressType::BILLING ) + ->withCustomerData( $customer_details ) + ->withDynamicDescriptor( sprintf( __( "Subscription payment for order: %s", "globalpayments-gateway-provider-for-woocommerce" ), $this->order->get_id() ) ) + ->execute(); + + return $response; + } + private function get_muti_use_token() { + return $this->order->get_meta( "_GP_multi_use_token" ) ? $this->order->get_meta( "_GP_multi_use_token" ) : $this->config['multi_use_token']; + } + private function get_shipping_details() { + $shipping_details = new Address(); + $shipping_details->streetAddress1 = $this->order->get_billing_address_1(); + $shipping_details->streetAddress2 = $this->order->get_billing_address_2(); + $shipping_details->city = $this->order->get_billing_city(); + $shipping_details->state = $this->order->get_billing_state(); + $shipping_details->postalCode = $this->order->get_billing_postcode(); + $shipping_details->country = $this->order->get_billing_country(); + + return $shipping_details; + } + protected function get_billing_address() { + $billing_address = new Address(); + $billing_address->streetAddress1 = $this->order->get_billing_address_1(); + $billing_address->streetAddress2 = $this->order->get_billing_address_2(); + $billing_address->city = $this->order->get_billing_city(); + $billing_address->state = $this->order->get_billing_state(); + $billing_address->postalCode = $this->order->get_billing_postcode(); + $billing_address->country = $this->order->get_billing_country(); + + return $billing_address; + } + private function get_customer_data() { + $customer = new Customer(); + $customer->id = $this->order->get_customer_id(); + $customer->firstName = Utils::sanitize_string( $this->order->get_billing_first_name() ); + $customer->lastName = Utils::sanitize_string( $this->order->get_billing_last_name() ); + $customer->email = $this->order->get_billing_email(); + $phone_code = CountryUtils::getPhoneCodesByCountry( $this->order->get_billing_country() ); + $customer->phone = new PhoneNumber( $phone_code[0], $this->order->get_billing_phone(), PhoneNumberType::HOME ); + + return $customer; + } +}