Przelewy internetowe

Internetowe przelewy bankowe

Internetowe przelewy bankowe są – obok kart – jedną z popularniejszych metod płatności. W Polsce przelewy pozostają najczęściej wybieraną metodą płatności online i część banków upraszcza proces płatności.

Wdrożenie przelewów internetowych

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.
Sample Request
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 przelewu: 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
$bt_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',
        ),
    ),
    'payment_type' => 'MT',
    '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'
        },
    },
    'payment_type' => 'MT',
    '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'
    },
  },
  'payment_type' : 'MT',
  '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ść / przekierowanie do banku

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

Przekierowanie do banku
PHP/Ruby/Python/Android
try {
    $status = $client->bankTransferSale($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.bank_transfer_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.bank_transfer_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.bankTransferSale(sale, customer, "http://example-page.com", PaymentType.MT, new Callback<PayPalAuthorizationResult>() {

    @Override
    public void onFinish(PayPalAuthorizationResult 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 płatności

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.
Sprawdzenie poprawności płatności
PHP/Ruby/Python/Android
$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_text']}");
        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_text"]}"
    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_text'))
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'
webview.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {

        if (url.contains(redirectUrl)) {
            try {
                Map<String, String> map = getQueryMap(new URL(url).getQuery());
                String salt = "YOUR_HASH_SALT";
                String status = map.get("status");
                String description = map.get("description");
                String amount = map.get("amount");
                String currency = map.get("currency");
                String id = map.get("id_sale");

                String calcHash = sha1(String.format("%1$s|%2$s|%3$s|%4$s|%5$s|%6$s", salt, status, description, amount, currency, id));

                // check hash salt
                if (!calcHash.equals(hash)) {
                    // Error, wrong hash
                }

                if (status.equals("ERROR")) {
                    String errorDescription=map.get("error_text");
                    // Error, transaction declined

                } else if (status.equals("CLEARED")) {
                    String idSale=map.get("id_sale");
                    // Success, transaction completed

                } else {
                        /* transaction pending:
                         * check status regularly using the saleStatus method
                         * or wait for notification */
                }

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else {
            view.loadUrl(url);
        }
        return true;
    }
});

public static Map<String, String> getQueryMap(String query) {
    String[] params = query.split("&");
    Map<String, String> map = new HashMap<String, String>();
    for (String param : params) {
        String name = param.split("=")[0];
        String value = param.split("=")[1];
        map.put(name, value);
    }
    return map;
}

private static String convertToHex(byte[] data) {
    StringBuilder buf = new StringBuilder();
    for (byte b : data) {
        int halfbyte = (b >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            buf.append((0 <= halfbyte) && (halfbyte <= 9) ? (char) ('0' + halfbyte) : (char) ('a' + (halfbyte - 10)));
            halfbyte = b & 0x0F;
        } while (two_halfs++ < 1);
    }
    return buf.toString();
}

public static String sha1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    MessageDigest md = MessageDigest.getInstance("SHA-1");
    md.update(text.getBytes("utf-8"), 0, text.length());
    byte[] sha1hash = md.digest();
    return convertToHex(sha1hash);
}

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 i sprawdzisz dodatkowe informacje lub ewentualne dane o błędach.
Ręczne sprawdzenie statusu transakcji
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 (gorąco do tego zachęcamy). 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:

Odbieranie notyfikacji o transakcjach
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?
    puts "Empty communication id"
    exit
end

# check if token is correct
unless 'YOUR_TOKEN' == params['token']
    puts "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
// Nie dotyczy...