Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authnet: accept.js support #3224

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

dingels35
Copy link

Authorize.net provides a way to capture credit cards without credit card data (number, expiration, etc) ever passing through the merchant's server. Using their accept.js client library, CC info is submitted directly to their server, which responds with a payment nonce. This nonce can then be sent safely though the merchant's server and used in authorize and purchase transactions. Full documentation here: https://developer.authorize.net/api/reference/features/acceptjs.html

This PR adds an OpaqueDataPaymentToken to handle authnet's payment nonce.

Given the following output from the API called made by the accept.js library:

{
  "opaqueData": {
    "dataDescriptor": "COMMON.ACCEPT.INAPP.PAYMENT",
    "dataValue": "eyJj...MSJ9"
  },
  "messages": {
    "resultCode": "Ok",
    "message": [{"code": "I00001", "text": "Successful."}]
  }
}

Authorization can be submitted like this:

token = OpaqueDataPaymentToken.new('eyJj...MSJ9', data_descriptor: 'COMMON.ACCEPT.INAPP.PAYMENT')
gateway = AuthorizeNetGateway.new(login: '', password: '')
gateway.authorize(123, token, order_id: 1)

Resolves #3187


% ruby -I test test/remote/gateways/remote_authorize_net_test.rb                           
Loaded suite test/remote/gateways/remote_authorize_net_test
Started
.....................................................................
Finished in 54.372302 seconds.
------------------------------------------------------------------------------------------------------------------------
69 tests, 238 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------------------------
1.27 tests/s, 4.38 assertions/s
% ruby -I test test/remote/gateways/remote_authorize_net_opaque_data_test.rb 
Loaded suite test/remote/gateways/remote_authorize_net_opaque_data_test
Started
.....
Finished in 6.58923 seconds.
------------------------------------------------------------------------------------------------------------------------
5 tests, 19 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------------------------
0.76 tests/s, 2.88 assertions/s
Loaded suite test/unit/gateways/authorize_net_test
Started
.................................................................................................
Finished in 0.251408 seconds.
------------------------------------------------------------------------------------------------------------------------
97 tests, 575 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
------------------------------------------------------------------------------------------------------------------------
385.83 tests/s, 2287.12 assertions/s

@dingels35 dingels35 force-pushed the authnet-accept-js branch 2 times, most recently from 629ba3d to 495368b Compare May 16, 2019 14:29
Copy link
Contributor

@therufs therufs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR -- it looks pretty good to me :D Hoping to get at least one more set of eyes on it for feedback.

lib/active_merchant/billing/opaque_data_payment_token.rb Outdated Show resolved Hide resolved
@curiousepic
Copy link
Contributor

curiousepic commented May 30, 2019

Hi @dingels35, still looking at this, but my first thought is whether the OpaqueDataPaymentToken the best name/abstraction. The name is a bit general; is the class also general enough to be used for similar implementations, or are there specifics that tie it to Authnet Accept.js's notion of a payment token?

@dingels35
Copy link
Author

@curiousepic - Good question. I definitely wanted this to be generic enough to handle other (possibly future) payment types.
There is an existing ApplePayPaymentToken that generates the exact same xml - <opaqueData><dataDescriptor>...</dataDescriptor><dataValue>...</dataValue></opaqueData>. I considered creating an OpaqueDataPaymentToken as a base for both an ApplePayPaymentToken and an AcceptJsPaymentToken, but I didn't really want to mess with existing apple pay functionality.

The way I have things set now, this could be used for apple pay scenarios. It can also be used for Visa Checkout and Android Pay (documentation links below). So I think having just one OpaqueData type that can be used for all 4 scenarios (accept.js, apple, android, visapay) is a good way to build support for multiple current, and possible future, authorize.net payment options.

https://developer.authorize.net/api/reference/index.html#visa-checkout
https://developer.authorize.net/api/reference/index.html#mobile-in-app-transactions-create-an-android-pay-transaction

@dingels35 dingels35 force-pushed the authnet-accept-js branch from 495368b to 66b0ac4 Compare May 30, 2019 15:09
@curiousepic
Copy link
Contributor

curiousepic commented May 30, 2019

@dingels35 I was actually thinking more about applications beyond Authnet, rather than other Authnet token types. If it's tied to Authnet, let's maybe make that explicit in the name.

@dingels35
Copy link
Author

@curiousepic - I like your thoughts. We could definitely add token classes for other payment types - AndroidPayPaymentToken, VisaCheckoutPaymentToken, and AcceptJsPaymentToken. Doing this would mostly require that each merchant consider each class separately.

Another option would be to allow the existing PaymentToken act on its own. I think I could use the same code listed in my PR summary to use a PaymentToken instead of an OpaqueDataPaymentToken. I'd likely just need to modify the PaymentToken class to something like this:

class PaymentToken
  attr_reader :payment_data, :metadata
  def initialize(payment_data, options = {})
    @payment_data = payment_data
    @metadata = options.with_indifferent_access
  end
  def type
    'payment_token'
  end
end

Exposing metadata would allow me to pull out the data_descriptor in gateways/authorize_net.rb. And I could also validate in that class that token.metadata[:data_descriptor] exists.

Thoughts on this approach? If you're not keen on it, I can also just rename that class to AuthorizeNetPaymentToken and keep this scoped to authnet only.

@curiousepic
Copy link
Contributor

@dingels35 Hi Cory, these are some interesting approaches. I think we should probably keep things slim here since we're approaching scope bloat, but I'd like to get some more perspectives for how to proceed from my colleagues and get back to you. @activemerchant/shopify-payments-devs may be interested as well.

Copy link
Contributor

@bayprogrammer bayprogrammer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things look very nice with the code and tests. I especially like the remote tests and the special gateway class to simulate the necessary front-end flow. Well done!

With respect to modeling the specialized token needed to support the Accept.js token type, I believe we'd be better off scoping these changes to only within the Authorize.net-relevant code, rather than expanding any top-level payment classes. We should be especially cautious with modifying the PaymentToken class directly right now.

There is precedence for the use of a nested gateway-specific token class within the StripeGateway class: StripeGateway::StripePaymentToken. Could we do something similar for the opaque data required by AuthorizeNetGateway? Perhaps AuthorizeNetGateway::OpaqueDataToken? Then we can move forward with minimal risk to existing code. We can always lift the class up to the top-level if and when we discover that this specific format of opaque data might be applicable to other gateways.

If that would work, the modifications to update the tests should be minimal, and it will be easier for us to reason about how it will interact with our own existing code when we go to integrate this patch into our internal systems.

@curiousepic
Copy link
Contributor

@bayprogrammer Nice, I like that path as well.

@taf2
Copy link
Contributor

taf2 commented Jun 26, 2019

what was wrong with #2422 ?

@bayprogrammer
Copy link
Contributor

what was wrong with #2422 ?

@taf2 I'm not aware that anything was necessarily wrong with it, but I'm guessing it simply didn't get picked up on our radar at the time. We've been making a renewed push at becoming more proactive in responding to issues and PRs.

@dingels35 How are things going on your side? Were my recommended changes something you can do?

@dingels35
Copy link
Author

@bayprogrammer - Thanks for checking back in. Your comments are good - I like that approach. I had some time off earlier in June and am heading out for a long weekend again. But I'll get this updated for you to look at in the next 2 weeks.

@bayprogrammer
Copy link
Contributor

@dingels35 Awesome, thanks so much. Have a great long weekend! 😄

@dfltr
Copy link

dfltr commented Oct 1, 2019

Hi all 👋 Would love to get this merged so we can all drop our Accept.js monkeypatches. Any updates on it?

@bayprogrammer
Copy link
Contributor

@dingels35 @dfltr I apologize I didn't see that this PR had been updated since I last looked at it. It's back on my radar and I will try to re-review and hopefully we can get this one merged in!

@paulsingh
Copy link

Hey guys, checking back in on this one: I'd love to use Accept.js via ActiveMerchant! :)

@taf2
Copy link
Contributor

taf2 commented Jul 29, 2020

My patch still works...

@ChrisNelsonBHG
Copy link

@bayprogrammer or @curiousepic 👋 I'm on @dingels35 's team, revisiting this issue. We'd love to drop our fork of ActiveMerchant if we can get this (or #4406) across the line.

Last we saw I think we were waiting on comments from @bayprogrammer. Is it realistic to revive this PR at this point, or how can we proceed?

@bayprogrammer
Copy link
Contributor

@ChrisNelsonBHG apologies, I am afraid I wasn't able to get back to this while I was still at Spreedly. I no longer work for Spreedly and I no longer have commit access to this project (nor would I remember enough about this accept.js business to be of much help any longer 😅).

@curiousepic
Copy link
Contributor

Marking this "of interest" before a cleanup of stale PRs

@Waylon87
Copy link

Waylon87 commented Apr 1, 2024

same: Marking this "of interest" before a cleanup of stale PRs

@taf2
Copy link
Contributor

taf2 commented Apr 1, 2024

Pretty sure we forked active_merchant long ago for this specific feature in authnet - seems pretty important feature...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Question: Is accept.js supported?
9 participants