8

Trying to set up iOS and PHP payment system using Braintree.

I can set up a clientToken with

$clientToken["client_token"] = Braintree_ClientToken::generate());
return ($clientToken);

And I can process a first payment with:

$result = Braintree_Transaction::sale(array(
        'amount' => '1',
        'paymentMethodNonce' => $nonce,
        'customer' => array(
            'id' => 'testId',
            'firstName' => 'John',
            'lastName' => 'Doe',
            'email' => 'john@doe.com',
        ),
        'options' => array(
            'submitForSettlement' => true,
            'storeInVaultOnSuccess' => true,
        )
      ));

However when I try to process a second payment I get the error:

91609 – Customer ID has already been taken.

How can I process a second payment for the same customer with the same customerId ('testId') - why does it throw an error when I try to pass the payment with an existing customer ID? Surely it should just attach the payment to that same customer? Isn't that what it's there for?

Edit: So after looking around a bit more I found another field I can include in the Braintree_Transaction::sale as follows:

'customerId' => 'testId',

So this will allow me to re-use a customerId stored in the Braintree vault. However for a first time transaction I get the error:

91510 – Customer ID is invalid.

So I end up in a catch 22 - I can use the first set of code for a new customer but not repeat customers and I can use the second for repeat customers but not new. I cannot use both together. So my solution is to create my own local database entry that determines whether a user has paid via braintree before or not and substitute the code accorrdingly. Is there a more streamlined approach?

contool
  • 1,034
  • 3
  • 18
  • 29

2 Answers2

7

I work at Braintree. If you need more help, you can always reach out to our support team.

You've got the right idea. You need track on your side whether a customer ID exists with Braintree.

There is an alternative, but I don't recommend it, as it takes an extra API call.

You can first try to create the customer with Braintree, ignoring an error if the error code is 91510:

$result = Braintree_Customer::create(array(
        'id' => 'testId',
        'firstName' => 'John',
        'lastName' => 'Doe',
        'email' => 'john@doe.com',
));

Then, you know that either the customer already existed, or you just created it, and you can use your second method to create a transaction.

agf
  • 171,228
  • 44
  • 289
  • 238
  • thanks @agf - I'll reach out to support if I run into any issues. I've noticed that there are some holes in the documentation, if you're able to point out to the documentation team - particularly my first bit of code above, the documentation is missing the fact that you have to create an array with item 'client_token' rather than just return the object otherwise iOS wont accept it. – contool Apr 03 '15 at 14:14
  • 4
    I feel documentation is little sparse too. It is not intuitive either. So far Stackoverflow support is good :) – java_dude May 12 '15 at 08:36
  • I think it would be nice to get two levels of errors. A payment error and a creation error in Braintree_Transaction::sale. Then we could create customers, payment methods and more without sacrificing the sale in case there were a duplicate. – Maciek Semik Oct 26 '15 at 00:38
  • @MaciekSemik You can, by [setting the "store in vault" option to "true"](https://developers.braintreepayments.com/reference/request/transaction/sale/ruby#options.store_in_vault). – agf Oct 26 '15 at 01:13
  • @agf do you know if this procedure still stands? Is there any improvement on being about to "create or use existing" in one call? – digout Nov 06 '20 at 10:51
0

There is other alternative, instead of looking in your database, you can use find('a_customer_id') in order to see if Braintree has already the user id. And then chose your first approach or the second one.

Gerardo
  • 168
  • 2
  • 8