SOFORT Banking

Wprowadzenie do SOFORT Banking

SOFORT Banking to międzynarodowy system płatności online dostępny w wybranych krajach europejskich (Polska, Niemcy, Austria, Szwajcaria, Wielka Brytania, Belgia, Francja, Włochy, Hiszpania).

Wdrożenie płatności SOFORT

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");

Transakcja

Zacznij od przygotowania danych do przeprowadzenia płatności z użyciem systemu SOFORT: informacje o transakcji, kliencie, 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/Android
$sofort_params = array(
    'sale' => array(
        'amount'      => 19.99,
        'currency'    => 'PLN',
        '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',
);
sofort_params = {
    'sale' => {
        'amount'      => 19.99,
        'currency'    => 'PLN',
        'description' => 'Product #1'
    },
    'customer' => {
        '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'
}
bt_params = {
  'sale' : {
    'amount'      : 19.99,
    'currency'    : 'PLN',
    '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'
}
Sale sale         = new Sale(19.99, "EUR", "Product #1");
Address address   = new Address("1600 Pennsylvania Avenue Northwest", "Washington", "DC", "500", "US");
Customer customer = new Customer("John Doe", "john@doe.com", "127.0.0.1", address);

Płatność

Po prostu wywołaj metodę sofortSale. 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 sofortSale 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ę sofortSale.

Realizacja transakcji
PHP/Ruby/Python/Android
try {
    $status = $client->sofortSale($bt_params);
}
catch (Exception $e) {
    // handle exceptions here
}

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.sofort_sale(bt_params)
rescue PayLane::ClientError => e
    # handle exceptions 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.sofort_sale(bt_params)
except Exception, e:
    # handle exceptions here

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()
api.sofortSale(sale, customer, "http://example-page.com", new Callback<RedirectSaleResult>() {

    @Override
    public void onFinish(RedirectSaleResult result) {
        WebView webview =...;
        webview.loadUrl(result.getRedirectUrl());
    }

    @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.
    }
});

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'


# 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')

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ń.

Oto przykład jak możesz sprawdzić komunikację i odebrać powiadomienie:

Sprawdzenie danych
PHP/Ruby/Python/Android
// 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')
// Nie dotyczy...

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.
    }
});