12

Copied directly from Braintree's tutorial, you can create a client token with a customer ID like this:

gateway.clientToken.generate({
    customerId: aCustomerId
}, function (err, response) {
    clientToken = response.clientToken
});

I declare var aCustomerId = "customer" but node.js closes with the error

new TypeError('first argument must be a string or Buffer')

When I try to generate a token without the customerId, everything works fine (though I never get a new client token but that's another question).

EDIT: Here is the complete test code as requested:

var http = require('http'),
    url=require('url'),
    fs=require('fs'),
    braintree=require('braintree');

var clientToken;
var gateway = braintree.connect({
    environment: braintree.Environment.Sandbox,
    merchantId: "xxx", //Real ID and Keys removed
    publicKey: "xxx",
    privateKey: "xxx"
});

gateway.clientToken.generate({
    customerId: "aCustomerId" //I've tried declaring this outside this block
}, function (err, response) {
    clientToken = response.clientToken
});

http.createServer(function(req,res){
   res.writeHead(200, {'Content-Type': 'text/html'});
   res.write(clientToken);
   res.end("<p>This is the end</p>");
}).listen(8000, '127.0.0.1');
Rob
  • 14,746
  • 28
  • 47
  • 65

1 Answers1

26

Disclaimer: I work for Braintree :)

I'm sorry to hear that you are having trouble with your implementation. There are a few things that might be going wrong here:

  1. If you specify a customerId when generating a client token, it must be a valid one. You do not need to include a customer id when creating a client token for a first time customers. Typically you would create create a customer when handling the submission of your checkout form, and then store that customer id in a database for use later. I'll talk to our documentation team about clarifying the documentation around this.
  2. res.write takes a string or a buffer. Since you were writing response.clientToken, which was undefined because it was created with an invalid customer id, you were receiving the first argument must be a string or Buffer error.

Some other notes:

  • If you create a token with an invalid customerId, or there is another error processing your request, response.success will be false, you can then inspect the response for the reason why it failed.
  • You should generate your client token within the http request handler, this will allow you generate different tokens for different customers, and to better handle any issues that result from your request.

The following code should work, provided you specify a valid customerId

http.createServer(function(req,res){
  // a token needs to be generated on each request
  // so we nest this inside the request handler
  gateway.clientToken.generate({
    // this needs to be a valid customer id
    // customerId: "aCustomerId"
  }, function (err, response) {
    // error handling for connection issues
    if (err) {
      throw new Error(err);
    }

    if (response.success) {
      clientToken = response.clientToken
      res.writeHead(200, {'Content-Type': 'text/html'});
      // you cannot pass an integer to res.write
      // so we cooerce it to a string
      res.write(clientToken);
      res.end("<p>This is the end</p>");
    } else {
      // handle any issues in response from the Braintree gateway
      res.writeHead(500, {'Content-Type': 'text/html'});
      res.end('Something went wrong.');
    }
  });

}).listen(8000, '127.0.0.1');
Nick Tomlin
  • 28,402
  • 11
  • 61
  • 90
  • @Rob correct. Typically for a first time customer you would not have an id (which is perfectly fine), but for returning customers the id would be stored in your database alongside that customer's details and you would want to use that when generating your client token. – Nick Tomlin Nov 18 '14 at 15:32
  • 3
    I am having the same issue for the first time customer. But I want the code just work for both first time and return customers! If I include the customerId, it throw error for first time customer, but if I do not include it, the return customer will not have the save credit card. So how do I implement this correctly?! – Hugh Hou Sep 11 '15 at 01:45
  • 1
    @HughHou Unfortunately there is no way to do this without adding some logic to handle both new and existing customers. Typically you would store the braintree `customerId` on a user model in your application's datastore. You could then check whether a user has a braintree customer id in your controller; if they do, you would include in the options to `clientToken.generate` otherwise you create the braintree customer, save it to your datastore, and use it to generate the client token. – Nick Tomlin Sep 11 '15 at 15:33
  • @NickTomlin This is also not the best practice, because customers could be deleted on the BT control panel manually. I recommend to try to find the customer first (https://developers.braintreepayments.com/reference/request/customer/find/node) and act accordingly. (Include the customerId in generate only if the customer really exists) – xnagyg Oct 25 '15 at 10:22
  • @NickTomlin, there should be extra param asking user, if user does not exist, should we just create it, if set to true. braintree should automatically create new user, rather then throwing error. anyway I'm going to create work around it, but it would have been nice if you guys had this option. – Basit Dec 02 '15 at 00:15
  • You might want to add a note saying that commenting out the customerId parameter applies to all the server side technologies. – Nagama Inamdar May 05 '16 at 13:41
  • http://stackoverflow.com/questions/39716674/regarding-integration-of-braintree-payment-gateway-and-java – Vivek Shankar Sep 27 '16 at 10:57
  • @Nick Tomlin Customer ID is "User UID"? – user1872384 Mar 23 '18 at 02:36
  • @user1872384 that’s a typical pattern but it can be any identifier within your domain – Nick Tomlin Mar 23 '18 at 11:51
  • 1
    This should be made part of the official documentation which is not very clear in this regard. – deekay42 Nov 15 '18 at 04:33