iDEAL

Wprowadzenie do iDEAL

iDEAL to holenderski system umożliwiający przyjmowanie płatności w Holandii z użyciem przelewów bankowych.

W przeciwieństwie do wielu innych lokalnych systemów przelewowych, iDEAL charakteryzuje się zunifikowanym interfejsem do bezpiecznego przetwarzania płatności w czasie rzeczywistymi.

iDEAL jest bardzo popularny na rynku holenderskim.

Wdrożenie płatności IDEAL

Zanim zaczniesz wywoływać metody naszego API, pamiętaj, aby poprawnie zainicjować PayLane Rest Client. To bardzo łatwe, po prostu załącz odpowiedni plik oraz podaj swoją nazwę użytkownika i hasło.

PayLane Rest Client
PHP/Ruby/Python/Android
include_once ('PayLaneRestClient.php');
$client = new PayLaneRestClient('your_login', 'your_password');
require 'paylane_client'
client = PayLane::Client.new('your_login', 'your_password')
from client import PayLaneRestClient
client = PayLaneRestClient("your_login", "your_password")
PayLaneApi api = PayLaneClientFactory.createClassicClient(context, "your_login", "your_password");

Lista kodów banków

Aby wyświetlić klientom formularz płatniczy, przez który będą mogli zapłacić z użyciem systemu iDEAL, musisz najpierw pobrać listę dostępnych banków. Służy do tego metoda idealBankCodes.

Zacznij od odpytania PayLane o liste banków (jak w przykładzie po prawej stronie)

Teraz możesz wyświetlić swoim klientom opcje płatności (może się to różnć w zależności od języka programowania / frameworku). W przykładzie po prawej możesz zobaczyć próbkę kodu.

Gdy formularz zostanie wysłany, należy wywołać metodę ideal/sale, co pozwoli na przeprowadzenie transakcji.

Lista banków
PHP/Ruby/Python
 try {
    $status = $client->idealBankCodes();
  }
  catch (Exception $e) {
    // handle exception
  }
  
  if (!$client->isSuccess()) {
    $error_number = $status['error']['error_number'];
    $error_description = $status['error']['error_description'];
    // handle error
  }
begin
  status = client.ideal_bank_codes()
rescue PayLane::ClientError => e
  # handle exception here
end
try:
  status = client.ideal_bank_codes()
except Exception, e:
  # handle exception
  print e
  sys.exit()
Opcje płatności
PHP/Ruby/Python
<select name="bank-code">
  <?php
    foreach ($status['data'] as $bank) {
      echo sprintf('<option value="%s">%s</option>', $bank['bank_code'], $bank['bank_name']), "\n";
    }
  ?>
</select>
if client.success?
  status['data'].each do |bank|
    # inject the returned bank data into your payment form, example:
    puts '<option value="%s">%s</option>' % [bank['bank_code'], bank['bank_name']]
  end
else
    puts "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
if client.is_success():
  for bank in status['data']:
    # inject the returned bank data into your payment form, example:
    print '<option value="%s">%s</option>' % (bank['bank_code'], bank['bank_name'])
else:
    sys.exit('Error number: ' + str(status["error"]["error_number"]) + '\n' \
             'Error description: ' + str(status["error"]["error_description"]))

Transakcja

Zacznij od przygotowania danych do przeprowadzenia płatności z użyciem systemu iDEAL:

  1. informacje o transakcji
  2. kliencie
  3. typie płatności

Zdefiniuj także adres (back_url), na który klient zostanie przekierowany po dokonaniu płatności.

Przygotowanie danych do transakcji
PHP/Ruby/Python
$params = array(
    'sale' => array(
        'amount'      => 19.99,
        'currency'    => 'EUR',
        'description' => 'Product #1'
    ),
    'customer' => array(
        'name'    => 'John Doe',
        'email'   => 'john@doe.com',
        'ip'      => '127.0.0.1',
        'address' => array (
            'street_house' => '1600 Pennsylvania Avenue Northwest',
            'city'         => 'Washington',
            'state'        => 'DC',
            'zip'          => '500',
            'country_code' => 'US',
        ),
    ),
    'back_url'     => 'http://example-page.com',
    'bank_code'    => 'INGBNL2A',
);
params = {
    'sale' => {
        'amount'      => 19.99,
        'currency'    => 'EUR',
        'description' => 'Product #1'
    },
    'customer' => {
        'name'    => 'John Doe',
        'email'   => 'john@doe.com',
        'ip'      => '127.0.0.1',
        'address' => {
            'street_house' => '1600 Pennsylvania Avenue Northwest',
            'city'         => 'Washington',
            'state'        => 'DC',
            'zip'          => '500',
            'country_code' => 'US'
        }
    },
    'back_url'  => 'http://example-page.com',
    'bank_code' => 'INGBNL2A'
}
params = {
  'sale' : {
    'amount'      : 19.99,
    'currency'    : 'EUR',
    'description' : 'Product #1'
  },
  'customer' : {
    'name'    : 'John Doe'
    'email'   : 'john@doe.com',
    'ip'      : '127.0.0.1',
    'address' : {
      'street_house' : '1600 Pennsylvania Avenue Northwest',
      'city'         : 'Washington',
      'state'        : 'DC',
      'zip'          : '500'
      'country_code' : 'US'
    },
  },
  'back_url'     : 'http://example.com',
  'bank_code'    : 'INGBNL2A'
}

Płatność

Po prostu wywołaj metodę idealSale. Możesz także sprawdzić, czy płatność się powiodła, pozyskać numer ID transakcji lub informacji o błędzie, jeśli coś pójdzie nie tak.

Jeśli metoda idealSale została wywołana z powodzeniem, możesz przekierować klienta na stronę banku, gdzie dokona płatności. Użyj adresu URL zwróconego przez metodę idealSale.

Realizacja transakcji
PHP/Ruby/Python
try
{
    $status = $client->idealSale($params);
}
catch (Exception $e)
{
    // handle exception
}

if ($client->isSuccess()) {
    echo "Success, transaction initiated, id_sale: {$status['id_sale']}, \n
           redirect_url: {$status['redirect_url']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}

header('Location: ' . $status['redirect_url']);
die;
begin
    status = client.ideal_sale(params)
rescue PayLane::ClientError => e
    # handle exception here
end

if client.success?
    puts "Success, transaction initiated, id_sale: #{status["id_sale"]}"\
         "redirect_url: #{status["redirect_url"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end

# redirect to url in status['redirect_url']
exit
try:
    status = client.ideal_sale(params)
except Exception, e:
    # handle exception
    print e
    sys.exit()

if client.is_success():
    print 'Success, transaction initiated, id_sale: %s, redirect_url: %s' % \
        (status['id_sale'], status['redirect_url'])
else:
    sys.exit('Error ID: ' + str(status["error"]["id_error"]) + '\n' \
             'Error number: ' + str(status["error"]["error_number"]) + '\n' \
             'Error description: ' + str(status["error"]["error_description"])) 

# redirect to url in status['redirect_url']
sys.exit()


# Uwaga dot. Pythona:
# W Pythonie nie ma natywnej funkcji służącej do przekierowania na inną stronę – musisz albo skorzystać z funkcji oferowanych przez framework, z którego korzystasz, albo napisać własną funkcję.

# W przypadku Django możesz użyć:
# from django.http import HttpResponseRedirect
# HttpResponseRedirect('http://example.com/')
# W przypadku Pylons możesz użyć:
# from pylons.controllers.util import redirectredirect('http://example.com/')

Weryfikacja

Po dokonaniu płatności na stronie banku, klient zostanie przekierowany z powrotem na Twoją stronę (na adres back_url). Powinieneś teraz zweryfikować zwrócone dane, aby uniknąć ewentualnych prób oszustwa i sprawdzić status transakcji.

Zweryfikowanie danych
PHP/Ruby/Python
$salt        = 'YOUR_HASH_SALT';
$status      = $_GET['status'];
$description = $_GET['description'];
$amount      = $_GET['amount'];
$currency    = $_GET['currency'];
$hash        = $_GET['hash'];

$id = '';
if ($status !== 'ERROR') // success, get id_sale
    $id = $_GET['id_sale'];

$calc_hash = sha1("{$salt}|{$status}|{$description}|{$amount}|{$currency}|{$id}");

// check hash salt
if ( $calc_hash !== $hash ) {
    die ("Error, wrong hash");
}

// check transaction status
switch ($status) {
    case 'ERROR':
        die("Error, transaction declined, {$_GET['error_description']}");
        break;

    case 'CLEARED':
        echo "Success, transaction completed, id_sale: {$_GET['id_sale']}";
        break;

    default:
        /* transaction pending: 
         * check status regularly using the saleStatus method 
         * or wait for notification */
        echo "Transaction pending";
        break;
}
# Simple controller action code in Rails
# it's just an example - most of the logic should be moved to model

salt        = 'YOUR_HASH_SALT'
status      = params['status']
description = params['description']
amount      = params['amount']
currency    = params['currency']
hash        = params['hash']

id = ''
unless status == 'ERROR'
    id = params['id_sale']
else
    # redirect to an index action to correct the payment + simple notice
    # for rails: redirect_to :index, :notice => "Error, transaction declined, #{description}"
end

calc_hash = Digest::SHA1.hexdigest("#{salt}|#{status}|#{description}|#{amount}|#{currency}|#{id}")

unless calc_hash == hash
    # redirect to an index action to correct the payment
    # for rails: redirect_to :index
end

# check transaction status

case status
    when 'ERROR'
        # redirect to an index action to correct the payment + simple notice
        # for rails: redirect_to :index, :notice => "Error, transaction declined, #{response["error"]["error_description"]}"
    when 'CLEARED'
        # redirect to an index action to correct the payment + simple notice
        # for rails: redirect_to :index, :notice => "Success, transaction completed, id_sale: #{id}"
    else
        # redirect to an index action to correct the payment + simple notice
        # for rails: redirect_to :index, :notice => "Transaction pending"
end
salt        = 'YOUR_HASH_SALT'
status      = get_request_param('status')
description = get_request_param('description')
amount      = get_request_param('amount')
currency    = get_request_param('currency')
hash        = get_request_param('hash')
id_sale     = None

# success, get id_sale
if status != 'ERROR':
    id_sale = get_request_param('id_sale')

calc_hash = hashlib.sha1(
    '|'.join([salt, status, description, amount, currency, id_sale])).hexdigest()

# check hash salt
if calc_hash != hash:
    sys.exit('Error, wrong hash')

# check transaction status
if status == 'ERROR':
    sys.exit('Error, transaction declined, %s' % \
        get_request_param('error_description'))
elif status == 'CLEARED':
    print 'Success, transaction completed, id_sale: %s' % id_sale
else:
    # transaction pending: check status regularly using the saleStatus
    # method or wait for notification
    print 'Transaction pending'


# Python note:
# The get_request_param function is supposed to collect data from GET params. Depending on your framework or any other toolkit, please use a proper function or write one that suits you best.

# For Django, you can use:
# param_from_get = request.GET.get('param_name')
# For Pylons, you can use:
# from pylons import requestparam_from_get = request.GET.get('param_name')

Sprawdzanie transakcji (ręczne)

Sprawdź status transakcji wywołując metodę getSaleInfo. Teraz możesz sprawdzić, czy dana transakcja się powiodła. Wystarczy, że wywołasz metodę isSuccess czy sprawdzisz dodatkowe informacje lub ewentualne dane o błędach.

Sprawdzenie danych
PHP/Ruby/Python/Android
try {
    $status = $client->getSaleInfo(array('id_sale' => $id_sale));
}
catch (Exception $e) {
    // handle exceptions here
}

if ($client->isSuccess()) {
    if ('CLEARED' === $status['status']) {
        echo "Success, transaction completed, id_sale: {$id_sale}";
    }
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
begin
    status = client.get_sale_info({"id_sale" => id_sale})
rescue PayLane::ClientError => e
    # handle exceptions here
end

if client.success?
    puts "Success, transaction completed, id_sale: #{id_sale}" if status['status'] == 'CLEARED'
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
try:
    status = client.get_sale_info({'id_sale': id_sale})
except Exception, e:
    # handle exceptions here

if client.is_success():
    if status['status'] == 'CLEARED':
        print 'Success, transaction completed, id_sale: %s' % id_sale
else:
    sys.exit('Error ID: ' + str(status["error"]["id_error"]) + '\n' \
             'Error number: ' + str(status["error"]["error_number"]) + '\n' \
             'Error description: ' + str(status["error"]["error_description"]))
api.saleInfo(idSale, new Callback<SaleInfoResult>() {

    @Override
    public void onFinish(SaleInfoResult result) {
        // success
    }

    @HandleException
    public void onProtocolError(ProtocolException e) {
        // invoke if not success
        // e.getCode() - error code
        // e.getMessage() - error message
    }

    @Override
    public void onError(Exception e) {
        // connection error etc.
    }
});

Sprawdzanie transakcji (automatyczne)

Zamiast sprawdzać statusy transakcji ręcznie, możesz po prostu skorzystać z mechanizmu powiadamiania o transakcjach. Nasze systemy same wyślą Ci stosowne informacje z użyciem POST – nie musisz wywoływać żadnych funkcji ani wysyłać żądań. Zobacz przykład aby dowiedzieć się jak możesz sprawdzić komunikację i odebrać powiadomienie.
Sprawdzenie danych
PHP/Ruby/Python
// check communication
if (empty($_POST['communication_id'])) {
    die('Empty communication id');
}

// check if token correct
if ('YOUR_TOKEN' !== $_POST['token']) {
    die('Wrong token');
}

foreach ($_POST['content'] as $notification) {
    if ($notification['type'] === 'S') { // sale created
        // transaction completed, do something useful with id_sale
        $id_sale = $notification['id_sale'];
    }
}

// return communication_id
die($_POST['communication_id']);
# check communication
if params['communication_id'].blank?
    print "Empty communication id"
    exit
end

# check if token is correct
unless 'YOUR_TOKEN' == params['token']
    print "Wrong token"
    exit
end

@content = params['content']
# You can do something useful in Rails view later on

@content.each do |notification|
    @id_sale = notification['id_sale'] if notification['type'] == 'S'
end

# render :text => params['communication_id']
# check communication
communication_id = get_request_param('communication_id')
if not communication_id:
    sys.exit('Empty communication id')

# check if token correct
token = get_request_param('token')
if token != 'YOUR_TOKEN':
    sys.exit('Wrong token')

#content = get_request_param('content')
content = [{'type': 'S', 'id_sale': 123456789}]
for notification in content:
    if notification['type'] == 'S':  # sale created
        # transaction completed, do something useful with id_sale
        id_sale = notification['id_sale']

# print communication_id


# Uwaga dot. Pythona: 
# Funkcja get_request_param ma za zadanie pobrać wartości GET. Użyj odpowiednich funkcji oferowanych przez framework, z którego korzystasz, lub napisz własną funkcję. 

# W przypadku Django możesz użyć:
# param_from_get = request.GET.get('param_name')
# W przypadku Pylons możesz użyć:
# from pylons import request
# param_from_get = request.GET.get('param_name')