/** * scheduled_subscription_payment * * Hooked to woocommerce_scheduled_subscription_payment_{gateway_id} * Completes recurring payments for a subscription */ public function scheduled_subscription_payment($amount_to_charge, $order) { $this->log(__FUNCTION__, "Info: Beginning processing of scheduled payment for order {$order->id} for the amount of {$amount_to_charge}"); $this->log(__FUNCTION__, "Info: Merchant ID = {$this->merchant_id}"); // token is required $payment_method_token = get_post_meta($order->id, '_wc_paypal_braintree_payment_method_token', true); if (empty($payment_method_token)) { $this->log(__FUNCTION__, "Error: Payment method token is missing on order meta"); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); return; } // as is the customer id $braintree_customer_id = get_post_meta($order->id, '_wc_paypal_braintree_customer_id', true); if (empty($braintree_customer_id)) { $this->log(__FUNCTION__, "Error: Braintree customer ID is missing on order meta"); WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); return; } // Create the gateway instance require_once dirname(__FILE__) . '/../braintree_sdk/lib/Braintree.php'; $gateway = new Braintree_Gateway(array('accessToken' => $this->merchant_access_token)); // Process the sale with the stored token and customer $sale_args = apply_filters('wc_gateway_paypal_braintree_sale_args', array('amount' => $amount_to_charge, 'paymentMethodToken' => $payment_method_token, 'recurring' => true, 'customerId' => $braintree_customer_id, 'channel' => 'WooThemes_BT', 'orderId' => $order->id, 'options' => array('submitForSettlement' => true, 'storeInVaultOnSuccess' => true))); try { $result = $gateway->transaction()->sale($sale_args); } catch (Exception $e) { $this->log(__FUNCTION__, 'Error: Unable to process scheduled payment. Reason: ' . $e->getMessage()); return false; } if (!$result->success) { $this->log(__FUNCTION__, "Error: Unable to process scheduled payment: {$result->message}"); return false; } $transaction_id = $result->transaction->id; $this->log(__FUNCTION__, "Info: Successfully processed schedule payment, transaction id = {$transaction_id}"); WC_Subscriptions_Manager::process_subscription_payments_on_order($order); $this->log(__FUNCTION__, "Info: Completed processing of scheduled payment for order {$order->id}"); $order->add_order_note(sprintf(__('PayPal Braintree charge complete (Charge ID: %s)', 'woocommerce-gateway-paypal-braintree'), $transaction_id)); $order->payment_complete($transaction_id); }
function testGatewayCreateTransactionUsingNonce() { $http = new Braintree_HttpClientApi(Braintree_Configuration::$global); $nonce = $http->nonce_for_new_card(array("creditCard" => array("number" => "4111111111111111", "expirationMonth" => "11", "expirationYear" => "2099"), "share" => true)); $gateway = new Braintree_Gateway(array('environment' => 'development', 'merchantId' => 'integration_merchant_id', 'publicKey' => 'integration_public_key', 'privateKey' => 'integration_private_key')); $result = $gateway->transaction()->sale(array('amount' => '47.00', 'paymentMethodNonce' => $nonce)); $this->assertTrue($result->success); $transaction = $result->transaction; $this->assertEquals(Braintree_Transaction::AUTHORIZED, $transaction->status); $this->assertEquals(Braintree_Transaction::SALE, $transaction->type); $this->assertEquals('47.00', $transaction->amount); }
/** * Process a refund if supported * @param int $order_id * @param float $amount * @param string $reason * @return boolean True or false based on success, or a WP_Error object */ public function process_refund($order_id, $refund_amount = null, $reason = '') { $this->log(__FUNCTION__, "Info: Beginning processing refund/void for order {$order_id}"); $this->log(__FUNCTION__, "Info: Merchant ID = {$this->merchant_id}"); $order = wc_get_order($order_id); if (!$this->can_refund_order($order)) { $this->log(__FUNCTION__, "Error: Unable to refund/void order {$order_id}. Order has no transaction ID."); return false; } if (!$refund_amount) { $refund_amount = floatval($order->get_total()); } $this->log(__FUNCTION__, "Info: Amount = {$refund_amount}"); $transaction_id = $order->get_transaction_id(); require_once dirname(__FILE__) . '/../braintree_sdk/lib/Braintree.php'; $gateway = new Braintree_Gateway(array('accessToken' => $this->merchant_access_token)); // See if the transaction is not yet settled and should be voided instead of refunded // If for some reason the transaction cannot be found, an Exception will be thrown try { $transaction = $gateway->transaction()->find($transaction_id); } catch (Exception $e) { $this->log(__FUNCTION__, "Error: Unable to find transaction with transaction ID {$transaction_id}. Reason: " . $e->getMessage()); return false; } $this->log(__FUNCTION__, "Info: Order {$order_id} with transaction ID {$transaction_id} has status {$transaction->status}"); // Ref: https://developers.braintreepayments.com/reference/request/transaction/void/php // Ref: https://developers.braintreepayments.com/reference/request/transaction/refund/php $action_to_take = ''; switch ($transaction->status) { case Braintree_Transaction::AUTHORIZED: case Braintree_Transaction::SUBMITTED_FOR_SETTLEMENT: case Braintree_Transaction::SETTLEMENT_PENDING: $action_to_take = "void"; break; case Braintree_Transaction::SETTLED: case Braintree_Transaction::SETTLING: $action_to_take = "refund"; break; } if (empty($action_to_take)) { $this->log(__FUNCTION__, "Error: The transaction cannot be voided nor refunded in its current state: state = {$transaction->status}"); return false; } // Only void transaction when refund amount equals to order's total. if ('void' === $action_to_take && $refund_amount != $order->get_total()) { return new WP_Error('unable_to_void', __('Unable to void unsettled transaction when refunding partially.', 'woocommerce-gateway-paypal-braintree')); } try { if ("void" === $action_to_take) { $result = $gateway->transaction()->void($transaction_id); } else { $result = $gateway->transaction()->refund($transaction_id, $refund_amount); } } catch (Exception $e) { $this->log(__FUNCTION__, 'Error: The transaction cannot be voided nor refunded. Reason: ' . $e->getMessage()); return false; } if (!$result->success) { $this->log(__FUNCTION__, "Error: The transaction cannot be voided nor refunded - reason: = {$result->message}"); return false; } // Get the refund/void transaction ID $latest_transaction_id = $result->transaction->id; if ("void" === $action_to_take) { $order->add_order_note(sprintf(__('Voided - Void ID: %s - Reason: %s', 'woocommerce-gateway-paypal-braintree'), $latest_transaction_id, $reason)); $this->log(__FUNCTION__, "Info: Successfully voided order {$order_id}"); } else { $order->add_order_note(sprintf(__('Refunded %s - Refund ID: %s - Reason: %s', 'woocommerce-gateway-paypal-braintree'), wc_price($refund_amount), $latest_transaction_id, $reason)); $this->log(__FUNCTION__, "Info: Successfully refunded {$refund_amount} for order {$order_id}"); } return true; }
/** * Cancel authorization * * @param int $order_id */ public function cancel_payment($order_id) { $order = wc_get_order($order_id); if (in_array($order->payment_method, array('paypalbraintree_cards', 'paypalbraintree_paypal'))) { require_once dirname(__FILE__) . '/braintree_sdk/lib/Braintree.php'; $trans_id = get_post_meta($order_id, '_transaction_id', true); $token = get_option('wc_paypal_braintree_merchant_access_token', ''); $gateway = new Braintree_Gateway(array('accessToken' => $token)); $transaction_details = false; if ($trans_id) { try { $transaction_details = $gateway->transaction()->find($trans_id); } catch (Exception $e) { $this->log(__FUNCTION__, "Error: Unable to find transaction with transaction ID {$trans_id}. Reason: " . $e->getMessage()); $order->add_order_note(__('Unable to void charge!', 'woocommerce-gateway-paypal-braintree') . ' ' . $e->getMessage()); return; } } if ($trans_id && 'authorized' === $transaction_details->status) { try { $result = $gateway->transaction()->void($trans_id); $order->add_order_note(sprintf(__('PayPal Braintree charge voided (Charge ID: %s)', 'woocommerce-gateway-paypal-braintree'), $result->transaction->id)); delete_post_meta($order->id, '_pp_braintree_charge_captured'); delete_post_meta($order->id, '_transaction_id'); } catch (Exception $e) { $this->log(__FUNCTION__, 'Error: Unable to void charge. Reason: ' . $e->getMessage()); $order->add_order_note(__('Unable to void charge!', 'woocommerce-gateway-paypal-braintree') . ' ' . $e->getMessage()); } } } }
function testHandlesEuropeBankAccounts() { $gateway = new Braintree_Gateway(array('environment' => 'development', 'merchantId' => 'altpay_merchant', 'publicKey' => 'altpay_merchant_public_key', 'privateKey' => 'altpay_merchant_private_key')); $result = $gateway->customer()->create(); $this->assertTrue($result->success); $customer = $result->customer; $clientApi = new Braintree_HttpClientApi($gateway->config); $nonce = $clientApi->nonceForNewEuropeanBankAccount(array("customerId" => $customer->id, "sepa_mandate" => array("locale" => "de-DE", "bic" => "DEUTDEFF", "iban" => "DE89370400440532013000", "accountHolderName" => "Bob Holder", "billingAddress" => array("streetAddress" => "123 Currywurst Way", "extendedAddress" => "Lager Suite", "firstName" => "Wilhelm", "lastName" => "Dix", "locality" => "Frankfurt", "postalCode" => "60001", "countryCodeAlpha2" => "DE", "region" => "Hesse")))); $transactionResult = $gateway->transaction()->sale(array("customerId" => $customer->id, "paymentMethodNonce" => $nonce, "merchantAccountId" => "fake_sepa_ma", "amount" => 100)); $this->assertTrue($transactionResult->success); $collection = $gateway->transaction()->search(array(Braintree_TransactionSearch::customerId()->is($customer->id), Braintree_TransactionSearch::europeBankAccountIban()->is("DE89370400440532013000"))); $this->assertEquals(1, $collection->maximumCount()); $this->assertEquals($transactionResult->transaction->id, $collection->firstItem()->id); }
function testGatewayRejectionOnApplicationIncomplete() { $gateway = new Braintree_Gateway(array('clientId' => 'client_id$development$integration_client_id', 'clientSecret' => 'client_secret$development$integration_client_secret')); $result = $gateway->merchant()->create(array('email' => '*****@*****.**', 'countryCodeAlpha3' => 'USA', 'paymentMethods' => array('credit_card', 'paypal'))); $gateway = new Braintree_Gateway(array('accessToken' => $result->credentials->accessToken)); $result = $gateway->transaction()->sale(array('amount' => '4000.00', 'creditCard' => array('number' => '4111111111111111', 'expirationDate' => '05/20'))); $this->assertFalse($result->success); $transaction = $result->transaction; $this->assertEquals(Braintree_Transaction::APPLICATION_INCOMPLETE, $transaction->gatewayRejectionReason); }