braintree / braintree_php_example

An example Braintree integration for PHP
MIT License
126 stars 94 forks source link

Integrate recurring subscription #45

Closed jamminjames closed 5 years ago

jamminjames commented 5 years ago

General information

Issue description

See below.

jamminjames commented 5 years ago

I read the closed issue on this here, and have read the recurring subscription guide at Braintree. I have this example code working for a simple transaction, but I want to be able to have the customer start a recurring subscription, and don't see how to get the paymentMethodToken. As per the Braintree guide, I am using this code:

$result = $gateway->subscription()->create([
  'paymentMethodToken' => 'the_token',
  'planId' => 'silver_plan'
]);

...dropping in my own created plan id. However, I don't understand how to get the paymentMethodToken for the newly created subscription, so we can go on to do the transaction, and let the customer pay for it immediately. The provided code returns an error, since the token is not provided. ("Error: 91903: Payment method token is invalid.")

I am testing this in our sandbox, of course.

Thanks for any help.

Epreuve commented 5 years ago

@jamminjames It sounds like you're skipping the step to create a payment method token. The payment method nonce generated by the example is a single use representation of a tokenized payment, while a payment method token is created using a nonce, and is used to charge a customer's payment method in a future transaction without requiring them to re-enter their credentials, whether that's a subscription, or a one-off transaction.

You'll want to make sure you're using the payment method nonce generated by the checkout form to first create a customer with a payment method or add a payment method to an existing customer, then use the payment method token representing the now vaulted payment method in the subscription create call.

If you have any further questions, reach out to Braintree support and they should be able to help get this solved with you.

jamminjames commented 5 years ago

I've got that working, now, how do you finalize the transaction after doing that? When I try:

$result = $gateway->transaction()->sale([
    'amount' => $amount,
    'paymentMethodNonce' => $nonce,
    'options' => [
        'submitForSettlement' => true
    ]
]);

... I get an error that you can't use the nonce twice. Without it, I get "Cannot determine payment method."

Or, without that transaction segment, I get a "no transaction id" error.

This is the full code I'm trying:

$result = $gateway->customer()->create([
    'firstName' => 'Joe',
    'lastName' => 'Smo',
    'company' => 'Smo Co.',
    'paymentMethodNonce' => $nonce
    ]);
    if ($result->success) {
        echo($result->customer->id);
        echo($result->customer->paymentMethods[0]->token);
        $pymtToken=$result->customer->paymentMethods[0]->token;
        } else {
            foreach($result->errors->deepAll() AS $error) {
                echo($error->code . ": " . $error->message . "\n");
            }
        }

    $result = $gateway->subscription()->create([
    'paymentMethodToken' => $pymtToken,
    'planId' => $planId,
    'options' => ['startImmediately' => true]
    ]);

$result = $gateway->transaction()->sale([
    'amount' => $amount,
    'paymentMethodNonce' => $nonce,  // what to do here?
    'options' => [
        'submitForSettlement' => true
    ]
]);
Epreuve commented 5 years ago

Like I mentioned, a payment method nonce is single use, so when you create a customer with a payment method, it's consumed.

That being said, is your intent to charge your customer twice? When you create a subscription, unless there is a trial period or a future billing date, it will immediately charge the customer. Attempting to create a transaction after a subscription would result in two charges to the customer.

If you do have a future billing date or a trial period on the subscription, or would still like to charge the customer twice (once when the subscription is create and once in the transaction) then you can replace paymentMethodNonce with paymentMethodToken within the sale(..) call and use the payment method token from the customer create(..) call to create the transaction.

jamminjames commented 5 years ago

Ok. But, when I leave out the transaction section, I get the error about no transaction id:

"Fatal error: Uncaught InvalidArgumentException: expected transaction id to be set in C:\xampp\apps\wordpress\subs.ht.new\vendor\braintree\braintree_php\lib\Braintree\TransactionGateway.php on line 507"

...and in the url, it's missing the id (http://localhost/transaction.php?id=)

Should I just eliminate the following code in the case of recurring subs? What should I put in its place?

if ($result->success || !is_null($result->transaction)) {
    $transaction = $result->transaction;
    header("Location: " . $baseUrl . "transaction.php?id=" . $transaction->id);
} else {
    $errorString = "";

    foreach($result->errors->deepAll() as $error) {
        $errorString .= 'Error: ' . $error->code . ": " . $error->message . "\n";
    }

    $_SESSION["errors"] = $errorString;
    header("Location: " . $baseUrl . "index.php");
}
Epreuve commented 5 years ago

If you change the behavior of the example to use subscriptions instead of transactions, you'll need to adjust the result handling code as well. Naturally, if you remove the code that creates a transaction, you'll need to adjust the snippet here to instead handle the subscription.

Since this isn't an issue with the example itself and is more so a question of building your integration, any follow up questions should be sent to Braintree support instead.

jamminjames commented 5 years ago

Ok, thanks for your help!