Embedded example - Card token

Frontend - Checkout form

To prevent card data being submitted by mistake the card input fields should not have name arguments but instead be identified by data-quickpay="cardnumber", data-quickpay="expiration" and data-quickpay="cvd"

When form is submitted card data will first be submitted directly to QuickPay for tokenization. If the tokenization is successful it will submit the form to your defined action with a card_token parameter which can be used to authorize the payment on your backend.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<form id="checkout-form" class="form-horizontal" action="/shop/pay" method="post">
  <label for="name">Name</label>
  <input type="text" name="name" value="">

  <label for="address">Address</label>
  <input type="text" name="address" value="">

  <label for="zipcode">Zipcode</label>
  <input type="text" name="zipcode" value="">

  <labe for="city">City</label>
  <input type="text" name="city" value="">

  <label>Card number</label>
  <div class="card">
    <div class="card-brand"></div>
    <input type="text" autocomplete="off" data-quickpay="cardnumber">
  </div>

  <label>Expiration</label>
  <input type="text" placeholder="MM / YY" autocomplete="off" data-quickpay="expiration">

  <label>CVV/CVD</label>
  <input type="text" maxlength="4" autocomplete="off" data-quickpay="cvd">

  <button class="btn btn-primary" type="submit">Pay</button>
</form>

<style type="text/css">
  .card {
    position: relative;
  }

  .card-brand {
    position: absolute;
    right: 5px;
    top: 5px;
    font-weight: bold;
  }

  input.error {
    border: 1px solid red;
  }
</style>

<script src="https://payment.quickpay.net/embedded/v2/quickpay.js"></script>
<script type="text/javascript">
  QuickPay.Embedded.Form(document.querySelector('#checkout-form'), {
    merchant_id: 1234,
    agreement_id: 1234,
    brandChanged: function(brand) {
      document.querySelector(".card-brand").innerHTML = brand;
    },
    beforeCreate: function(form) {
      var button = document.querySelector("#checkout-form button");
      button.setAttribute("disabled", "disabled");
      button.innerHTML = "Please wait...";

      var inputs = document.querySelectorAll("input.error");
      for (var i = 0; i < inputs.length; i++) {
        inputs[i].classList.remove("error");
      }
    },
    success: function(form, data) {
      return true; // Return false to prevent form submit
    },
    failure: function(form, type, message, data) {
      switch (type) {
        case "validation":
          for (var i = 0; i < data.length; i++) {
            document.querySelector('input[data-quickpay=' + data[i] + ']').classList.add('error');
          }
          break;
        default:
          alert(type + ': ' + message);
      }

      document.querySelector("#checkout-form button").innerHTML = "Pay";
    }
  });
</script>

Backend

These are simplified examples that will almost certainly not fit your shop system. However, it should give an idea on how to use the Embedded payment form.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
require 'sinatra/base'
require 'quickpay/api/client' # Find the QuickPay Ruby client at https://learn.quickpay.net/tech-talk/opensource

class App < Sinatra::Base
  enable :sessions

  post '/shop/pay' do
    order = Order.new(
      name:    params[:name],
      address: params[:address],
      zipcode: params[:zipcode],
      city:    params[:city]
    )

    order.save!

    # See https://learn.quickpay.net/tech-talk/api/services
    client = QuickPay::API::Client.new(api_key: ENV['QUICKPAY_API_KEY'])
    token  = params[:card_token]

    payment = client.post('/payments', {
      order_id: order.order_id, # MUST be unique
      currency: 'EUR'
    })

    order.payment_id = payment['id']
    order.save!

    payment = client.post("/payments/#{order.payment_id}/authorize?synchronized", {
      amount: 12345, # 123.45
      card: { token: token }
    })

    if payment['approved']
      order.authorized = true
    end

    order.qp_status_code = payment['operations'].last['qp_status_code']
    order.qp_status_msg  = payment['operations'].last['qp_status_msg']
    order.aq_status_code = payment['operations'].last['aq_status_code']
    order.aq_status_msg  = payment['operations'].last['aq_status_msg']
    order.save!

    if order.authorized?
      session[:order_id] = order.id
      redirect to('/shop/receipt')
    else
      erb :error
    end
  end

  get '/shop/receipt' do
    @order = Order.find(session[:order_id])
    erb :receipt
  end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php

/**
 * This is only an very simple mocked example.
 */

require_once 'QuickPay'; // Find the QuickPay PHP client at https://learn.quickpay.net/tech-talk/opensource

use QuickPay/QuickPay as Client;
use Models/Order;

class PaymentController
{
    public function pay(Request $request)
    {
        $order = new Order([
            'name'    => $request('name'),
            'address' => $request('address'),
            'zipcode' => $request('zipcode'),
            'city'    => $request('city')
        ]);

        $order->save();

        $client = new Client(":{$this->config('API_KEY')}");

        $token = $request('card_token');

        $payment = $client->request->post("/payments", [
            'order_id' => $order->order_id, // Must be unique
            'currency' => 'EUR'
        ])->as_object();

        $order->payment_id = $payment->id;
        $order->save();

        $payment = $client->request->post("/payments/{$order->payment_id}/authorize?synchronized", [
            'amount' => 12345,
            'card'   => [ 'token' => $token ]
        ]);

        if ($payment->accepted) {
            $order->authorized = true;
        }

        $authorize = end($payment->operations);

        $order->qp_status_code = $authorize->qp_status_code;
        $order->qp_status_msg  = $authorize->qp_status_msg;
        $order->aq_status_code = $authorize->aq_status_code;
        $order->aq_status_msg  = $authorize->aq_status_msg;
        $order->save();

        if ($order->authorized()) {
            $this->session('order_id', $order->id);
            $this->redirectTo('/payment/receipt');
        } else {
            $this->render('error');
        }
    }

    public function receipt()
    {
        $order = Order::find($this->session('order_id'));
        $this->render('receipt', compact('order'));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from flask import Flask, session, render_template, redirect, url_for, request
from flask.ext.session import Session

# manage session using Flask-Session
sessionProvider = Session()

# Initialize flask app
app = Flask("Quickpay")
sessionProvider.init_app(app)


@app.route('/shop/pay')
def pay():
    params = request.form
    order = Order(name=params['name'], address=params['address'],
                  zipcode=params['zipcode'], city=params['city'])
    order.save

    # See https://learn.quickpay.net/tech-talk/api/services
    client = QPClient(":%s" % os.environ['QUICKPAY_API_KEY'])
    token = params['card_token']

    payment = client.post('/payments',
                          order_id=order.order_id,  # MUST be unique
                          currency='EUR')

    order.payment_id = payment['id']
    order.save

    payment = client.post(
        "/payments/%s/authorize?synchronized" % order.payment_id,
        amount=12345,  # 123.45
        card={"token": token}
    )

    if payment['approved']:
        order.authorized = true

    order.qp_status_code = payment['operations'][-1]['qp_status_code']
    order.qp_status_msg = payment['operations'][-1]['qp_status_msg']
    order.aq_status_code = payment['operations'][-1]['aq_status_code']
    order.aq_status_msg = payment['operations'][-1]['aq_status_msg']
    order.save

    if order.authorized:
        session['order_id'] = order.id
        return redirect(url_for('/shop/receipt'))
    else:
        return render_template("error.html", order=order)


@app.route('/shop/receipt')
def show_receipt():
    order = Order.query.filter_by(id=session.get('order_id'))
    return render_template('receipt.html', order=order)