checkout flow and confirmation step

632 views
Skip to first unread message

Axel Perkins

unread,
Dec 5, 2013, 1:04:55 PM12/5/13
to spree...@googlegroups.com
Hello everybody

I have a question about the architecture of the checkout flow. 

We need to have a confirmation step, no matter what the payment method/gateway chosen. This is german law and not providing this last step (after choosing the payment method but before any actual paying takes place!) can lead to expensive juristical problems for the vendor.

There is a possibility to switch on the conformation by setting 
Spree::Config[:always_include_confirm_step]
to true and this will lead to an additional checkout step, as defined here:

go_to_state :confirm, if: ->(order) { order.confirmation_required? }

On the other hand, as far as I can see, the only place where the actual payment is triggered is here:

if states[:payment]
  before_transition
:to => :complete do |order|
    order
.process_payments! if order.payment_required?
 
end
end

That would mean, that as of now, the payment can never be delayed to after the confirmation step, which imho. renders the whole step useless.

Would you generally agree? Anything that I have missed?

Also, as far as I have understood, it should be possible to send the user to a remote page belonging to the payment gateway (i.e. paypal) to login and allow the payment there and then - without actually paying - returning to the shops confirmation page.
After confirmation here, the actual payment should be executed (via a token sent to the payment gateways API).
Is this correct in principle? Do active merchant and or spree embrace this pattern?

Axel

Peter Berkenbosch

unread,
Dec 5, 2013, 2:51:50 PM12/5/13
to Axel Perkins, spree...@googlegroups.com
I totally agree! 

On a older shop I “hacked” this to only redirect to the offsite payment step (similar to paypal) on the confirm step. I think I use that as inspiration for a better implementation.

--
Peter Berkenbosch
Developer
Spree Commerce Inc

Spree Commerce

@spreecommerce GitHub Open Source Facebook Google+

Ryan Bigg

unread,
Dec 5, 2013, 7:02:04 PM12/5/13
to spree...@googlegroups.com
This is a very curly issue because it brings in two very complex systems within Spree: the checkout flow and the payment processing. For hopefully very obvious reasons we don't want either of these two things to behave in weird and magical ways. Therefore, we must exercise extreme caution when making changes here.


On Fri, Dec 6, 2013 at 5:04 AM, Axel Perkins <tsch...@googlemail.com> wrote:
Also, as far as I have understood, it should be possible to send the user to a remote page belonging to the payment gateway (i.e. paypal) to login and allow the payment there and then - without actually paying - returning to the shops confirmation page.
After confirmation here, the actual payment should be executed (via a token sent to the payment gateways API).
Is this correct in principle? Do active merchant and or spree embrace this pattern?

Axel

Some gateways operate like this. Other gateways provide you with a token which we then store against the source (typically a Spree::CreditCard object) and then we can use that token later on to authorize/capture the payment. Stripe and Braintree are two gateways that I know of that support this. My Spree PayPal Express extension (https://github.com/radar/better_spree_paypal_express) actually has people confirm the payment on the PayPal side of things, but I am considering making that optional behaviour in the near future so that store owners can configure it to allow for confirmation on PayPal or confirmation on Spree. (https://github.com/radar/better_spree_paypal_express/issues/50

For gateways that do not provide a token, we must send the payment data *immediately* after the payment step, as that is where the payment information is only available (as it's within memory then and only then). This means that if we were to then make the user go to the confirm action and then from there go to a complete action where they could finalize the payment, the payment information that should be sent to the gateway (CC number, etc.) would be lost and we wouldn't be able to send it.

Therefore, if you are really necessarily needing this confirm step then you *must* use a gateway that provides Spree with some kind of token that it can use later on to capture that payment. Otherwise, there will be no confirmation step and you'll be in breach of those laws.

----

I think we may be able to get around that particular problem with some JavaScript trickery which would persist the CC details on the client-side, show an order confirmation page and then send the details to the server to complete the order. I think this may take a bit of work to do (around a week), but I also think it would be possible. If you desperately want a confirmation step and you're not using a gateway that provides you with a token, then I'd recommend investigating this route.

--

Ryan Bigg
Community Manager
Spree Commerce, Inc.

Peter Berkenbosch

unread,
Dec 6, 2013, 3:57:39 AM12/6/13
to Ryan Bigg, spree...@googlegroups.com
True words Ryan!

The process can stay the same, only the place where we execute the payment processing should be on a different more logical place, like the confirm step. IMHO

This should be done with extreme caution of course :)

--
Peter Berkenbosch
Developer
Spree Commerce Inc

Spree Commerce

@spreecommerce GitHub Open Source Facebook Google+


Message has been deleted

Peter Berkenbosch

unread,
Dec 6, 2013, 4:39:12 AM12/6/13
to Jonathan O'Connor, spree...@googlegroups.com
That’s totally possible, however the confirm step currently is not used for the terms&conditions confirmation, but to confirm to place the order. 

--
Peter Berkenbosch
Developer
Spree Commerce Inc

Spree Commerce

@spreecommerce GitHub Open Source Facebook Google+


On 6 Dec 2013 at 10:32:36, Jonathan O'Connor (nink...@gmail.com) wrote:

Peter,
I'm actually doing similar work on this now. You raise some issues I hadn't considered.
I added a confirmed_at timestamp to the spree_orders table. I set this to the current time, when the accept AGBs ( == Terms and Conditions) checkbox is ticked.
Then you know when they accepted the terms. The confirmation_required? method just returns confirmed_at.nil?

If your gateway is doing an immediate payment, could you not add an accept terms and conditions checkbox to the payment page?

Regards,
Jonathan

Ryan Bigg

unread,
Dec 8, 2013, 6:52:33 PM12/8/13
to spree...@googlegroups.com
You can add an "acceptance of terms and conditions" checkbox to this form using an easy combination of Deface + some Order model decorators (to validate the acceptance of that attribute, for instance).

Jonathan O'Connor

unread,
Dec 9, 2013, 4:53:50 AM12/9/13
to spree...@googlegroups.com
Ryan,
yes, that's basically what I did. But the difficult part is writing the validation. I added a confirmed_at column to Spree::Order. I couldn't just add a validation that confirmed_at can't be null, as the Order is updated many times during it's lifecycle. So, I had to do a bit of a hack. I created a non-persistent attribute in Spree::Order which I set when the confirmed_at field is set. Then I only validate the confirmed_at field when that flag has been set (AGBs are Terms and Conditions in German):

    def accepts_agbs
      confirmed_at.present?
    end

    def accepts_agbs=(value)
      self.confirmed_at = value == '1' ? Time.now : nil
      # This variable is set so that we run the validation for checking acceptance of the AGBs at validation time.
      # As this is only set here, it can only happen at the time when the order is updating from the confirm page.
      @requires_checking_agbs_accepted = true
    end

    def confirmation_required?
      confirmed_at.nil?
    end

    def ensure_agbs_accepted_at_confirm
      if @requires_checking_agbs_accepted && !accepts_agbs
        @requires_checking_agbs_accepted = false
        errors.add(:base, :must_accept))
      end
    end


I hope this helps anyone trying to do anything similar.
Regards,
Jonathan

Ryan Bigg

unread,
Dec 10, 2013, 10:37:36 PM12/10/13
to spree...@googlegroups.com
Why not use a "validates_acceptance_of :attr" validation instead?

Jonathan O'Connor

unread,
Dec 11, 2013, 12:32:30 PM12/11/13
to spree...@googlegroups.com
Because that will get run every time we want to save the order, before the order is even in the cart. But, yes there probably is a trick to using validates_acceptance_of, with an :if option.

Ryan Bigg

unread,
Dec 13, 2013, 1:12:59 AM12/13/13
to spree...@googlegroups.com
Yup, an if option would definitely be the way to go

Axel Perkins

unread,
Dec 16, 2013, 8:02:12 AM12/16/13
to spree...@googlegroups.com
@radar: Please, can you go into a little more detail here?

We use the Paymill Gateway (https://github.com/Shopify/active_merchant/blob/master/lib/active_merchant/billing/gateways/paymill.rb), that does support the two-step-process

Now, when we enforce the confirm step via always_include_confirm_step there is no communication with the paymill API after entering the CC data and submitting.

Only after the confirm step the authorize method is being called, resulting in an error from the gateway:
request+contains+no+creditcard%2C+bank+account+number+or+bank+name

The complete response object is:

#<ActiveMerchant::Billing::MultiResponse:0x007fec89951bb8>
Gateway Error
 
--- !ruby/object:ActiveMerchant::Billing::MultiResponse
responses
:
- !ruby/object:ActiveMerchant::Billing::Response
 
params:
    transaction
:
      account
:
        bin
: ''
        brand
: ''
        expiry
:
          month
: '01'
          year
: '2001'
        holder
: ''
        number
: "\r\n"
      channel
: 8a8394c48a83948b42ef22
        address
:
          country
: DE
        contact
:
          ip
: 78.99.00.99
          ipCountry
: de
      identification
:
        shortId
: 5664.9999.8098
        uniqueId
: 8a83948b42ef228a83948b42ef22
      mode
: LIVE
      payment
:
        code
: CC.RG
      processing
:
        code
: CC.RG.70.40
        reason
:
          code
: '40'
          message
: Account+Validation
        result
: NOK
       
return:
          code
: 100.100.100
          message
: request+contains+no+creditcard%2C+bank+account+number+or+bank+name
        timestamp
: 2013-12-16+12%3A52%3A47
      response
: SYNC
  message
: request+contains+no+creditcard%2C+bank+account+number+or+bank+name
  success
: false
  test
: false
  authorization
:
  fraud_review
:
  avs_result
:
    code
:
    message
:
    street_match
:
    postal_match
:
  cvv_result
:
    code
:
    message
:
primary_response
: :last

I redacted some numbers here but removed none. The take-away is: A lot of data, mainly the CC number, is missing. This should be expected, since we do not want to store sensitive data, right? (weirdly the expiration date is still there, I changed it in the above log).

Should the authorize call not have happened directly after filling in the credit card info form, when all data is still present?
How is this supposed to work?

Axel

Washington Luiz

unread,
Dec 16, 2013, 8:09:17 AM12/16/13
to spree...@googlegroups.com
I think Spree uses the response_code attr in payment to authorize / capture payments through gateway. And that response_code should be filled in right after customer submits the credit card info. Thats why it don't need the credit card info to talk to the payment gateway later.
--
Washington L Braga Jr

Developer
Spree Commerce, Inc.

Ryan Bigg

unread,
Dec 17, 2013, 5:05:22 PM12/17/13
to spree...@googlegroups.com
Axel: It would help if I could see this issue happening myself on my own machine. Can you please provide a list of steps that I can follow to see this thing happening?

Thanks!
--

---

Ryan Bigg
Community Manager
Spree Commerce, Inc.


Join us in New York City for SpreeConf 2014 February
 26th and 27th. Learn more

Use the code Spreeconf_RB to get 33% off!


Reply all
Reply to author
Forward
0 new messages