Stripe Payments

How to configure and adapt Aimeos based shops as developer
Forum rules
Always add your Aimeos and PHP version as well as your environment (Linux/Mac/Win)
Spam and unrelated posts will be removed immediately!
schoelli
Posts: 15
Joined: 25 Oct 2017, 17:21

Stripe Payments

Post by schoelli » 01 Dec 2017, 13:24

I have installed Aimeos 2017.10.2 and aimeos/ai-payments 2017.10.2 in my existing Laravel 5.4 project. When I perform a test payment I get the following message:
Screen Shot 2017-12-01 at 14.14.38.png
Screen Shot 2017-12-01 at 14.14.38.png (41.59 KiB) Viewed 6911 times
Looks like ai-payments uses a depricated Stripe API. How can I change it to the new API?

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: Stripe Payments

Post by aimeos » 01 Dec 2017, 16:22

According to the Strip documentation, there must be a "script" embedded into the payment from:
https://stripe.com/docs/checkout/tutorial#embedding

This will add a hidden input field named "stripeToken" to the form after the customer entered his payment data:
https://stripe.com/docs/checkout/tutorial#tokens

Then, you can use the token to charge the money:
https://github.com/thephpleague/omnipay-stripe#stripejs

There's already a Stripe class that extends from the Omnipay class where you can overwrite the methods that needs to be changed:
https://github.com/aimeoscom/ai-payment ... Stripe.php

There are a few things to do:
1.) You have to overwrite that template and add the "script" tag there:
https://github.com/aimeos/ai-client-htm ... efault.php
2.) Overwrite the process() method in the ai-payments Stripe class:
https://github.com/aimeoscom/ai-payment ... y.php#L381
3.) It should return a form object which contains the URL you get with $this->getConfigValue('payment.url-self') if the "stripeToken" isn't available:
https://aimeos.org/api/latest/class-Aim ... ndard.html
4.) The process() method must call the processOrder() method, but your have to overwrite the getData() method first, where you have to add the Stripe token:
https://github.com/aimeoscom/ai-payment ... y.php#L637

We would love to get a pull request for your changes to the ai-payments Stripe class :-)
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

sixbynine
Posts: 93
Joined: 10 Jan 2018, 11:22

Re: Stripe Payments

Post by sixbynine » 10 Jan 2018, 21:09

Hello,

I managed to work on this solution, it took me a long time to make it work because I'm new with Laravel and Aimeos (so please be kind, if you answer ^^) and because I wanted to replace the default credit card info form by the Stripe's card info form.

I finally got my first Stripe test payments working.

I'm replying to this post to have your point of view on what I did and to clarify some points because I still have many questions and doubts about it :) Also, it is the only post I found with the correct/working information to make Stripe work with Laravel / Aimeos.

I'm using:

"laravel/framework": "5.5.*",
"aimeos/aimeos-laravel": "2018.01.1",
"aimeos/ai-payments": "dev-master"

1/ I added this part to process-body-standard.php. It is from the Stripe documentation https://stripe.com/docs/stripe-js/elements/quickstart

Code: Select all

<div>
	<script src="https://js.stripe.com/v3/"></script>
	
	  <div class="form-row">
		<label for="card-element">
		  Credit or debit card
		</label>
		<div id="card-element">
		  <!-- a Stripe Element will be inserted here. -->
		</div>
	
		<!-- Used to display form errors -->
		<div id="card-errors" role="alert"></div>
	  </div>
	


	  <a class="btn btn-default btn-lg" href="<?= $enc->attr( $this->standardUrlPayment ); ?>">
				<?= $enc->html( $this->translate( 'client', 'Change payment' ), $enc::TRUST ); ?>
			</a>
			<button class="btn btn-default btn-lg btn-action" >PAY NOW</button>
</div>

<script>
	// Create a Stripe client
var stripe = Stripe('my_publishable_key');

// Create an instance of Elements
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    lineHeight: '18px',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

// Create an instance of the card Element
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>
card.mount('#card-element');

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

function stripeTokenHandler(token) {
  // Insert the token ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);
  form.appendChild(hiddenInput);

  // Submit the form
  form.submit();
}

// Handle form submission
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
  event.preventDefault();

  stripe.createToken(card).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the token to your server
      stripeTokenHandler(result.token);
    }
  });
});

</script>
> It added the Stripe form, when completed, it creates the hidden field stripeToken.

2/ I simply overwrited the getData() method this way (OmniPay.php#L637) -> 'token' => $params['stripeToken']

Code: Select all

	$data = array(
			'transactionId' => $orderid,
			'amount' => $this->getAmount( $base->getPrice() ),
			'currency' => $base->getLocale()->getCurrencyId(),
			'language' => $base->getAddress( \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT )->getLanguageId(),
			'description' => sprintf( $this->getContext()->getI18n()->dt( 'mshop', 'Order %1$s' ), $orderid ),
			'clientIp' => $this->getValue( 'client.ipaddress' ),
			'token' => $params['stripeToken']
		);
3/ I overwrited the process() method this way because card number and cvv are no more set, it worked but i'd like to know if I can do that without side effect...

Code: Select all

		if( $this->getValue( 'onsite' ) == true && ( !isset( $params['stripeToken'] )) ) {
			return $this->getPaymentForm( $order, $params );
		}
Then the test worked !

Notes / Questions :
1) I left all the card info blank for the test, because Stripe tokenized it and everything will be in the hidden input "stripeToken"... Can I do that? Do I need to keep the information not needed by Stripe (NAME, CVV, EXPIRY, NUMBER) somewhere for something? For me, it seems more secure to keep only the tokenized information but I'm not used to the checkout process.

2) I don't know why exactly, when I tried to remove the original aimeos credit card fields in process-body-standard.php, it resulted that when the process page is reached, the page reloads infinitely. How can I proper remove the fields from the page?

3) Of course, I need to move the <script> parts from the process-body-standard.php. Where can I add it? What is the good practice to load it just on the process page and when the DOM is ready?

4) I try to get more info about the 'onsite' value, but i'm still not sure about how it works.

I'm still learning, so don't be too abrupt if I made something wrong or if I ask stupid things :) !

Thank you a lot for aimeos and thank you a lot in advance for any help, answers, suggestions about this topic. I hope it will help someone else.

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: Stripe Payments

Post by aimeos » 11 Jan 2018, 14:25

sixbynine wrote: 1) I left all the card info blank for the test, because Stripe tokenized it and everything will be in the hidden input "stripeToken"... Can I do that? Do I need to keep the information not needed by Stripe (NAME, CVV, EXPIRY, NUMBER) somewhere for something? For me, it seems more secure to keep only the tokenized information but I'm not used to the checkout process.
Yes, that's the way it should be. You shouldn't store any credit card data locally when you have the token.
sixbynine wrote: 2) I don't know why exactly, when I tried to remove the original aimeos credit card fields in process-body-standard.php, it resulted that when the process page is reached, the page reloads infinitely. How can I proper remove the fields from the page?
The Omnipay service provider the Aimeos Stripe service provider is extending from defines the fields for the frontend (https://github.com/aimeoscom/ai-payment ... ay.php#L75) and adds the fields to a form object here: https://github.com/aimeoscom/ai-payment ... y.php#L709

You should overwrite that method in the Stripe service provider so it only returns the url, method Stripe token.
sixbynine wrote: 3) Of course, I need to move the <script> parts from the process-body-standard.php. Where can I add it? What is the good practice to load it just on the process page and when the DOM is ready?
I think, the best way is to add it at the end of the process-body-standard.php template. The required dom nodes are then already available.
sixbynine wrote: 4) I try to get more info about the 'onsite' value, but i'm still not sure about how it works.
The "onsite" parameter is used to display the form for collecting the payment data (e.g. credit card data) locally. You will need it but for displaying the Stripe form. Remove the fields you don't need like described above.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

gladgladwrap
Posts: 10
Joined: 27 Jan 2018, 22:10

Re: Stripe Payments

Post by gladgladwrap » 17 Mar 2018, 05:08

I am in the same situation (with Aimeos 2017.10.2) : I have not been able to process a successful test payment. This is probably because I'm not using only the Stripe Elements and I am sending additional credit card data directly to Stripe with the Aimeos input fields. I want to remove the original Aimeos credit card fields without the resulting infinite reload and instead use the Stripe Elements payment form. Also, the form provided by aimeos does not have an id for our JS to hook onto:

Code: Select all

var form = document.getElementById('payment-form');
I will include my process(), getData(), and getPaymentForm() methods from the ai-payments Stripe class (copied from ai-payments/lib/custom/src/MShop/Service/Provider/Payment/OmniPay.php). P.S. I made changes directly inside the Aimeos core for now.

My steps:

1. Overwrite the process-body-default.php template and configured config/shop.php to point to this template.

2. Add " <script src="https://js.stripe.com/v3/"></script> " to the head of the shop's base template so it will be active on all shop pages, as recommended by Stripe.

3. Add the Stripe Elements' HTML and JS to our process-body-default.php template, just after ul.form-list. I follow the steps here: https://stripe.com/docs/stripe-js/eleme ... tart#setup . Shouldn't we be using only the Stripe Elements instead of the Aimeos credit card input fields because only the Stripe Elements' fields are sent and returned as a Stripe token? And as I mentioned before, our JS is hooking onto a form 'id' that doesn't exist in my code since I have immersed the Stripe Elements within the Aimeos form. Do I create a form outside of the Aimeos form or alter the getPaymentForm() so that the AImeos credit card fields don't show and then modify the JS so that it hooks onto and listens to the original Aimeos form?

4. Add the Stripe token to the getData() method -- (Last line in the $data array).

Code: Select all

protected function getData( \Aimeos\MShop\Order\Item\Base\Iface $base, $orderid, array $params )
	{
		$data = array(
			'transactionId' => $orderid,
			'amount' => $this->getAmount( $base->getPrice() ),
			'currency' => $base->getLocale()->getCurrencyId(),
			'language' => $base->getAddress( \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT )->getLanguageId(),
			'description' => sprintf( $this->getContext()->getI18n()->dt( 'mshop', 'Order %1$s' ), $orderid ),
			'clientIp' => $this->getValue( 'client.ipaddress' ),
			'token' => $params['stripeToken']
		);

		if( $this->getValue( 'onsite', false ) || $this->getValue( 'address', false ) ) {
			$data['card'] = $this->getCardDetails( $base, $params );
		}

		return $data + $this->getPaymentUrls();
	}
5. Overrode the process() method at https://github.com/aimeoscom/ai-payment ... y.php#L381 in the Stripe class. As you said:
If the "stripeToken" isn't available, process() should return a form object which contains the URL you get with $this->getConfigValue('payment.url-self')
I have not called getConfigValue() for that situation because I don't know if other params are needed.

Code: Select all

public function process( \Aimeos\MShop\Order\Item\Iface $order, array $params = [] )
	{
	    if( $this->getValue( 'onsite' ) == true && ( !isset( $params['stripeToken'] )) ) {

    	//return $this->getPaymentForm( $this->getConfigValue( array( 'payment.url-self' ) ), $params );

        	return $this->getPaymentForm( $order, $params );
      }

		return $this->processOrder( $order, $params );
	}
6. The getPaymentForm() inside of our Omni service provider adds fields to a form object. I override it in the Stripe class and change the return to a URL, HTTP request method, and Stripe token (the rest of the code I did not change) :

Code: Select all

return new \Aimeos\MShop\Common\Item\Helper\Form\Standard( $url,  'POST', $params['stripeToken'] );
Where have I gone wrong with the configuration?
Are we supposed to add hidden fields to the Aimeos form or delete it and create our own form with Stripe Elements?
Any help is very much appreciated.
Thank you
PHP 7.1.14, Aimeos 2017.10, Laravel 5.5.39 (php artisan --version)

Travin
Posts: 70
Joined: 18 Dec 2017, 03:12

Re: Stripe Payments

Post by Travin » 05 Apr 2018, 15:12

Hi

Code: Select all

Do I create a form outside of the Aimeos form or alter the getPaymentForm() so that the AImeos credit card fields don't show and then modify the JS so that it hooks onto and listens to the original Aimeos form?
I faced a problem here too. The form at Aimeos is rendering by ext\ai-client-html\client\html\templates\checkout\standard\serviceattr-partial-standard.php
And it's creating <input> for each one field. The problem is Stripe does not allowing <input> tags for Stripe Elements forms. We have to use divs. And also we need input for token, input that will not display at order page for customer.
And yes, we also need to send data at Stripe firstly, and THEN submit the payment form.

So I have created JS that detect which payment provider is chosen, and if it has custom JS, it will manage form submitting process. If it don't have any custom JS, it will submit form immediately.

Code: Select all

4. Add the Stripe token to the getData() method
I have added verification of necessity of token at the current Payment provider, to be sure we will not break another payment providers based on OmniPay class

Code: Select all

if( $this->getValue( 'token', false ) ){
			$data['token'] = $params['paymenttoken'];
		}
Laravel 6.18.19 | php 7.4.7 | Xubuntu | Aimeos Laravel 2019.10.5

schoelli
Posts: 15
Joined: 25 Oct 2017, 17:21

Re: Stripe Payments

Post by schoelli » 08 May 2018, 12:13

Does the new version of Aimeos (2018.04) have support for stripe token payments?

User avatar
aimeos
Administrator
Posts: 7873
Joined: 01 Jan 1970, 00:00

Re: Stripe Payments

Post by aimeos » 11 May 2018, 05:35

The next version (2018.07) will have support for.
Professional support and custom implementation are available at Aimeos.com
If you like Aimeos, Image give us a star

Post Reply