braintree / braintree_node

Braintree Node.js library
https://developer.paypal.com/braintree/docs/start/overview
MIT License
334 stars 104 forks source link

bug: paymentMethod.create() with billingAddressId not working #194

Closed DwieDima closed 3 years ago

DwieDima commented 3 years ago

General information

Issue description

creating a payment method with existing billingAddressId not working. All values inside billing of paymentmethod is null.

this is the method i call:

gateway.paymentMethod
      .create({
        customerId: userData.braintreeCustomerId,
        billingAddressId: userData.braintreeAddressId,
        paymentMethodNonce
      })

this is the billing response object:


{"firstName":null,"lastName":null,"company":null,"streetAddress":null,"extendedAddress":null,"locality":null,"region":null,"postalCode":null,"countryName":null}

the success response object is true.

This Address is working on the customer object.

sestevens commented 3 years ago

Hi @DwieDima, I'm having trouble replicating this issue -- the billing address fields are not coming back as null for me. Could you share your code for creating the billing address?

Also, you mentioned that you're looking at the values inside billing, but I assume you mean response.paymentMethod.billingAddress? There is no billing field on paymentMethod.

DwieDima commented 3 years ago

Hi @sestevens.

my workflow is:

  1. when a user register to my application i create an braintree customer
      const braintreeUser = await gateway.customer.create({
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email
      });
  1. at the same time create an braintree address

await gateway.address.create({
        customerId: braintreeUser.customer.id,
        firstName: user.firstName,
        lastName: user.lastName,
        streetAddress: user.address.street + " " + user.address.houseNumber,
        locality: user.address.city,
        postalCode: user.address.postalCode,
        countryCodeAlpha2: user.address.country.code
      });
  1. then on the clientside the user creates an payment method (visa) using hosted fields (i dont provide a billingAddress here, because i want to do it on the backend --> thats why i created an address in step 2):

  public addCreditCard() {
    this.isLoading = true;
    this.hostedFieldsInstance.tokenize(
      {
        vault: true,
      },
      (tokenizeErr, payload) => {
        if (tokenizeErr) {
          console.log('tokenizeErr', tokenizeErr);
          this.isLoading = false;
          // this shows an error alert message
          this.onAddPaymentError();
        } else {
          console.log('nonce', payload);
          // this sends the payload none to my backend where i save the payment method
          this.savePaymentMethodAndDismissModal(payload.nonce).toPromise();
        }
      }
    );
  }
  1. next on the backend side i receive the payment nonce and add the billingAddressId which i created in step 2. customerId and billingAddressId is saved in my own database
    // link the addressId created on user registration
    return gateway.paymentMethod
      .create({
        customerId: userData.braintreeCustomerId,
        billingAddressId: userData.braintreeAddressId,
        paymentMethodNonce
      });

When i log the response of paymentMethod.create() i get the same response as i mentioned at the beginning (success: true, billingAddress fields null).

---UPDATE---

Now i added the user address on the clientside, too (step 3) and it works as expected:


  public addCreditCard() {
    const user = this.userService.getCurrentUser();
    this.isLoading = true;
    this.hostedFieldsInstance.tokenize(
      {
        vault: true,
        billingAddress: {
          firstName: user.firstName,
          lastName: user.lastName,
          streetAddress: user.address.street + ' ' + user.address.houseNumber,
          locality: user.address.city,
          postalCode: user.address.postalCode,
          countryCodeAlpha2: user.address.country.code,
        },
      },
      (tokenizeErr, payload) => {
        if (tokenizeErr) {
          console.log('tokenizeErr', tokenizeErr);
          this.isLoading = false;
          this.onAddPaymentError();
        } else {
          console.log('nonce', payload);
          this.savePaymentMethodAndDismissModal(payload.nonce).toPromise();
        }
      }
    );
  }

is this behavior intended so that if you do not specify the billingAddress on the client side, the addressBillingId is not taken into account on the backend side in paymentMethod.create()?

sestevens commented 3 years ago

@DwieDima Yes, this is expected behavior. Since you're passing vault: true on the client, the payment method is created with the information (such as billing address) that you provide. When you call paymentMethod.create on the server with payment method nonce that you vaulted on the client, it doesn't create a new payment method. Instead, it just returns the existing payment method with whatever information was originally provided on the client.

There are two options here:

  1. If you want to vault on the client by passing vault: true, without passing a billing address, you need to call paymentMethod.update on the server to add the billing address (not paymentMethod.create).
  2. If you want to vault on the server, don't pass vault: true on the client. Then you would call paymentMethod.create on the server and pass in the billing address.

Feel free to reach out to Braintree Support with any further questions!

DwieDima commented 3 years ago

@sestevens thank you for clarification!