braintree / braintree_python

Braintree Python library
https://developer.paypal.com/braintree/docs/start/overview
MIT License
242 stars 115 forks source link

Test nonces don't work? #86

Closed frnsys closed 7 years ago

frnsys commented 7 years ago

not sure what I'm doing wrong here, but I'm in a sandbox environment using nonce='fake-valid-nonce' here:

    resp = braintree.Subscription.create({
        'payment_method_nonce': nonce,
        'plan_id': config.BRAINTREE_SUBSCRIPTION_PLAN_ID
    })

and keep getting the error "Payment method nonce is invalid."

EvanHahn commented 7 years ago

Our fake nonces are effectively one-time-use, but subscription calls need a payment method that they can hit periodically.

Subscription.create() calls will only accept a nonce if it is directly tied to a payment method in your Vault. You can generate a nonce like this by using PaymentMethodNonce.create() and passing it a token from your Vault. For example:

nonce_create = braintree.PaymentMethodNonce.create('A_PAYMENT_METHOD_TOKEN')
nonce = nonce_create.payment_method_nonce.nonce

resp = braintree.Subscription.create({
    'payment_method_nonce': nonce,
    'plan_id': config.BRAINTREE_SUBSCRIPTION_PLAN_ID
})

This shouldn't affect you in production because you'll never use fake nonces.

crookedneighbor commented 7 years ago

In production, if you have the payment_method_token on hand, you do not need to first generate a nonce. You can pass the payment_method_token directly:

    'payment_method_token': 'A_PAYMENT_METHOD_TOKEN',
    'plan_id': config.BRAINTREE_SUBSCRIPTION_PLAN_ID
})

If you have a service that takes in a nonce that represents a vaulted payment method from the client (such as a nonce generated with a customer id via the Drop-in UI), you could use the braintree.PaymentMethodNonce.create to create a nonce that you would pass into that service for server side testing.

amandapouget commented 6 years ago

@EvanHahn Can you provide an example of what you mean by 'A PAYMENT METHOD TOKEN'?

I'm trying to test the same thing in Ruby (existing Customer, fills out credit card form, establishes subscription)... My server posts the Braintree::Subscription.create() and "works" in reality.

Just trying to get to a way to test it. :-)

crookedneighbor commented 6 years ago

@mandysimon88 Evan was explaining that if you must use a nonce in your subscription create call, you can generate a nonce from an existing payment method stored in your vault. The 'A_PAYMENT_METHOD_TOKEN' bit refers to a token for a payment method already stored in your vault.

In normal use cases though, there'd be no need to generate a nonce first, since you already have the token for the payment method. You can just pass that directly in the create call instead of the nonce. (See https://github.com/braintree/braintree_python/issues/86#issuecomment-294575976)

amandapouget commented 6 years ago

I found the solution to my problem by using the nonce_for_new_payment_method available from this spec helper, included with the Braintree gem: https://github.com/braintree/braintree_ruby/blob/master/spec/integration/braintree/client_api/spec_helper.rb

Recording this here in case anyone else is writing a Ruby controller test involving a Braintree::Subscription.

Here is my working example:

describe 'POST #create (success)' do
    let(:organization) { create(:organization) }
    let(:valid_card) do
      {
        number: "4111111111111111",
        expiration_month: "11",
        expiration_year: "2099"
      }
    end
    let(:nonce) do
      organization.update(
        braintree_customer_id: Braintree::Customer.create(company: organization.name).customer.id
      )
      nonce_for_new_payment_method(
       credit_card: valid_card,
       client_token_options: {customer_id: organization.braintree_customer_id}
     )
    end

    describe 'in Braintree' do
      it 'creates a BraintreeSubscription tied to our organization' do
        params = { payment_method_nonce: nonce, plan_id: 'monthly-recurring' }
        post :create, params: params
.....
           // in Controller...
           nonce_from_the_client = params[:payment_method_nonce]
           result = Braintree::Subscription.create(
             payment_method_nonce: nonce_from_the_client,
             plan_id: params[:plan_id]
           )
           result.success? // ...is true

Recommend updating this documentation page, which errantly suggests that the fake valid nonces can be using for recurring billing: https://developers.braintreepayments.com/guides/recurring-billing/testing-go-live/ruby