Karty

Wprowadzenie do kart płatniczych

Karty to jedne z najpopularniejszych i najwygodniejszych metod płatności na świecie. Klienci mogą używać różnego rodzaju kart do płacenia przez internet, w tym m.in. kart kredytowych, debetowych czy przedpłaconych (pre-paid). Karty stanowią dobry wybór dla większości rynków, a na niektórych wręcz dominują.  

Wdrażanie płatności kartą

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. Więcej informacji na temat integracji z naszymi systemami znajdziesz na stronie wdrażanie i testy.
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");

PayLane.js

PayLane.js to autorska biblioteka JavaScriptowa, której zadaniem jest ztokenizowanie danych kartowych, które Twój klient podaje na Twojej stronie. Dzięki niej dane dotyczące kart płatniczych Twoich klientów są wysyłane w tle do naszych serwerów, a my zwracamy Tobie token, którego możesz używać do realizowania płatności, czy autoryzacji.

Ściągnij bibliotekę PayLane.js stąd.

Przebieg płatności z użyciem PayLane REST.js:

  1. Kod HTML jest przesyłany do przeglądarki; strona jest wyświetlana.
  2. Klient PayLane.js zostaje zainicjowany i czeka na zatwierdzenie formularza płatności.
  3. Gdy formularz zostaje zatwierdzony, klient PayLane.js wysyła informacje o karcie do serwerów PayLane. W odpowiedzi generowany jest tymczasowy token dla karty (64-bajtowy heksadecymalny łańcuch znaków), który wstawiany jest do formularza płatności w formie ukrytego pola.
  4. Proces wysyłania formularza płatności jest wznawiany, a serwery sprzedawcy otrzymują token wraz z pozostałymi informacjami z formularza (z wyłaczaniem wrażliwych danych karty).
  5. Sprzedawca może wywołać metodę sale /authorization/checkCar/checkCard3DSecure używając wygenerowanego tokenu (musi to nastąpić w ciągu 15 minut od wygenerowania tokenu; po upływie tego czasu traci on ważność).

PayLane.js: Inicjalizacja

Oto prosty przykład typowego formularza płatności:

<form id="checkout-form" action="" type="">
    <!-- merchant's input elements, as many as required -->
    <input type="text" name="first-name" value="">
    <input type="text" name="last-name" value="">
    <input type="text" name="email" value="">
    <input type="text" name="address" value="">

    <!-- card related input elements: -->
    <input type="text" value="" data-paylane="cc-number">
    <input type="text" value="" data-paylane="cc-expiry-month">
    <input type="text" value="" data-paylane="cc-expiry-year">
    <input type="text" value="" data-paylane="cc-cvv">
    <input type="text" value="" data-paylane="cc-name-on-card">

    <input type="submit" value="submit">
</form>
Rzeczywisty kod formularza można dowolnie zmieniać według własnych potrzeb, należy jednak spełniać przy tym poniższe wymagania:
  1. Pola dotyczące danych kart muszą mieć ustawione stosowne atrybuty data-paylane.
  2. Pola dotyczące danych kart nie mogą mieć atrybutów name. To wymóg bezpieczeństwa, który zapobiega wysłaniu danych kart do serwerów sprzedawcy.
  3. Formularz płatniczy musi miec nadany unikalny atrybut id. Klient PayLane.js będzie się posługiwał tą wartością.
W następnej kolejności zainicjalizuj klienta PayLane.js:

<script src="path/to/paylane.js"></script>
<script>
    try
    {
        var client = new PayLaneClient({
            publicApiKey : 'PUBLIC_API_KEY',
            paymentForm  : 'checkout-form',
        });
    }
    catch (e)
    {
        console.log(e); // exceptions are fatal
    }
</script>
Klient PayLane.js wymaga podania jedynie publicznego klucza API oraz wskazania na formularz płatniczy.
  • klucz API jest 40-znakowym łańcuchem znaków, który można znaleźć w Merchant Panelu PayLane (Administracja -> Integracja -> Podstawowe -> Integracja API)
  • na formularz płatniczy można wskazać przekazując jako argument jedną z poniższych wartości:
    1. atrybut id formularza
    2. węzeł drzewa DOM, który reprezentuje formularz (np. osiągalny przez document.forms[i])
    3. obiekt jQuery zawierający jedynie formularz płatniczy
Poniższe wartości mogą być opcjonalnie przekazane do klienta PayLane.js (jeśli nie zostaną zdefiniowane, zostaną użyte wartości):
  1. cardNumberInputName (domyślnie: cc-number) – alternatywna wartość data-paylane odnosząca się do pola numeru karty
  2. cardExpiryMonthInputName (domyślnie: cc-expiry-month) – alternatywna wartość data-paylane odnosząca się do pola miesiąca ważności karty
  3. cardExpiryYearInputName (domyślnie: cc-expiry-year) – alternatywna wartość data-paylane odnosząca się do pola roku ważności karty
  4. cardSecurityvarInputName (domyślnie: cc-cvv) – alternatywna wartość data-paylane odnosząca się do pola kodu CVV2/CVC2
  5. cardHolderInputName (domyślnie: cc-name-on-card) – alternatywna wartość data-paylane odnosząca się do pola z imieniem i nazwiskiem posiadacza karty
  6. errorTypeInputName (domyślnie: paylane_error_type) – alternatywna wartość name odnosząca się do pola typu błędu
  7. errorCodeInputName (domyślnie: paylane_error_code) – alternatywna wartość name odnosząca się do pola kodu błędu
  8. errorDescriptionInputName (domyślnie: paylane_error_description) – alternatywna wartość name odnosząca się do pola opisu błędu
  9. tokenInputId (domyślnie: paylane-token) – alternatywna wartość id odnosząca się do pola tokenu
  10. tokenInputName (domyślnie: paylane_token) – alternatywna wartość name odnosząca się do pola tokenu
  11. errorHandler (domyślnie: pusta funkcja) – funkcja obsługi błędu, przyjmuje trzy argumenty: type, code, description
  12. callbackHandler (domyślnie: pusta funkcja) – opcjonalna funkcja obsługująca zatwierdzenie formularza. Zostanie ona wywołana w momencie, gdy zakończy się wykonywanie żądania AJAX z tokenem. Token pojawi się w formularzu jako ukryte pole i zostanie przekazany do funkcji jako jedyny argument. Jeśli funkcja nie zostanie określona, zatwierdzenie formularza będzie miało standardowy przebieg.
    
    /**
    Custom token callback handler.
    
    @param {string} token Temporary credit card token
    @return {void}
    */
    callbackHandler: function(token){}
    

PayLane.js: Obsługa błędów

PayLane.js może wywoływać wyjątki i błędy.

Wyjątki mogą powstać jedynie podczas inicjalizacji klienta, np. gdy wymagany formularz lub jego pola nie zostaną znalezione lub został podany pusty klucz publiczny. Takie wyjątki muszą być obsłużone standardowo z użyciem try/catch, aczkolwiek nie powinny wystepować, jeśli tylko formularz został prawidłowo przygotowany.

Z kolei błędy mogą występować niespodziewanie z różnych przyczyn, takich jak przerwy w połączeniu sieciowym, źle sformatowane dane (np. token czy numer karty) itp. Klient PayLane.js umożliwia obsługe błędów na dwa sposoby: z użyciem funkcji obsługi błędów (przekazywanej do konstruktora jako errorHandler) i jako ukryte pola formularza (które można obsłużyć po stronie serwera).

  1. Obsługa błędów:
    
    /**
     * Custom error handler which allows the merchant to
     * handle errors raised by the PayLaneClient class,
     * such as connection errors or erroneous API responses.
     * 
     * @param  {int}    type          Error type from PayLaneClient.errorTypes
     * @param  {int}    [code]        Error code from the API response
     * @param  {string} [description] Error description from the API response
     * @return {void}
     */
    function errorHandler(type, code, description)
    {
        console.log(type, code, description);
    }
    
  2. Ukryte pola formularza:

    Poniższe ukryte pola zostaną dodane do formularza płatniczego (należy pamietać ich, że ich nazwy mogą zostać nadpisane podczas przekazywania parametrów error* do konstruktora):

    • paylane_error_type: typ błędu; 1, jeśli to błąd połączenia, 2, jeśli to błąd zwrócony przez API REST.js PayLane
    • paylane_error_code: występuje tylko w przypadku błędów API (kod zwracany przez API REST.js)
    • paylane_error_description: występuje tylko w przypadku błędów API (opis zwracany przez API REST.js)

Pojedyncza transakcja

Przygotuj dane potrzebne do przeprowadzenia płatności jak w przykładzie poniżej. Jak widzisz, w przypadku płatności kartą potrzebne są trzy zestawy informacji: dotyczące transakcji (sale), klienta (customer) oraz karty (card).

Następnie wykonaj płatność używając metody cardSaleByToken.

Możesz sprawdzić, czy płatność się powiodła, wywołując metodę isSuccess. Pozyskanie numeru ID transakcji (lub informacji o błędzie, jeśli coś pójdzie nie tak), jest również bardzo proste i może wyglądać jak na poniższym przykładzie.

Dokładne opisy wszystkich metod i struktur znajdziesz w opisie funkcji.

Próbka kodu - przygotowanie danych
PHP/Ruby/Python/Android
$card_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',
        ),
    ),
    'card' => array(
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4',
     ),
);
card_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'
        }
    },
    'card' => {
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
    }
}
card_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'
    }
  },
  'card' : {
    'token' : '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
  }
}
Sale sale = new Sale(
    19.99, 
    "EUR", 
    "Product #1"
);
Card card = new Card(
    "4111111111111111", 
    "03", 
    "2017", 
    "John Doe", 
    "123"
);
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
);
Próbka kodu - wykonanie płatności (cardSaleByToken)
PHP/Ruby/Python/Android
try {
    $status = $client->cardSaleByToken($card_params);
} catch (Exception $e) {
    // handle exceptions here
}   

// checking transaction status example (optional):
if ($client->isSuccess()) {
    echo "Success, id_sale: {$status['id_sale']} \n"; 
} 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.card_sale_by_token(card_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking transaction status example (optional):
if client.success?
    puts "Success, id_sale: #{status["id_sale"]}"
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.card_sale_by_token(card_params)
except Exception, e:
    # handle exceptions here

# checking transaction status example (optional):
if client.is_success():
    print 'Success, id_sale: %s' % status['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.cardSale(sale, customer, card, new Callback<CardSaleResult>() {

    @Override
    public void onFinish(CardSaleResult 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.
    }

Autoryzacja

Możesz nie chcieć od razu pobrać pieniędzy z karty klienta, ale jedynie autoryzować jego kartę. To dość użyteczne w wielu przypadkach, takich jak:

  • pobieranie numeru karty podczas zakładania darmowego konta próbnego w Twojej usłudze (później można już automatycznie obciążać kartę),
  • sprawdzanie karty (np. czy są dostępne środki lub czy transakcje internetowe nie są zablokowane),
  • potwierdzenie numeru karty, gdy klient tworzy konto w Twoim e-sklepie (np. jeśli chcesz mu udostępnić płatności single-click).

Autoryzacja karty skutkuje zablokowaniem określonej kwoty na karcie klienta (na pewien czas). Możesz zablokować całą kwotę danej transakcji (i pobrać środki później) lub zablokować tylko 1 zł (lub $1, €1, £1…), aby jedynie zweryfikować kartę.

Przygotuj dane potrzebne do autoryzacji karty. Będą to identyczne informacje jak w przypadku “zwykłej” płatności kartą.

Mając te dane, po prostu wywołaj metodę cardAuthorizationByToken (podobnie jak w przypadku płatności wywołasz cardSale).

Możesz łatwo sprawdzić, czy autoryzacja się powiodła oraz sprawdzić numer ID autoryzacji – możesz go potem użyć do ponownych obciążeń karty (resale), jeśli chcesz wykorzystać np. płatności cykliczne lub single-click.

Przygotowanie danych do autoryzacji
PHP/Ruby/Python/Android
$card_params = array(
    'sale' => array(
        'amount'      => 1.00,
        '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',
        ),
    ),
    'card' => array(
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
     ),
);
card_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'
        }
    },
    'card' => {
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
    }
}
card_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'
    }
  },
  'card' : {
    'token' : '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
  }
}
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);
Card card = new Card("4111111111111111", "03", "2017", "John Doe", "123");
Realizacja autoryzacji
PHP/Ruby/Python/Android
try {
    $status = $client->cardAuthorizationByToken($card_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking authorization status example (optional):
if ($client->isSuccess()) {
    echo "Success, id_authorization: {$status['id_authorization']} \n"; 
} 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.card_authorization_by_token(card_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking authorization status example (optional):
if client.success?
    puts "Success, id_authorization: #{status["id_authorization"]}"
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.card_authorization_by_token(card_params)
except Exception, e:
    # handle exceptions here

# checking authorization status example (optional):
if client.is_success():
    print 'Success, id_authorization: %s' % status['id_authorization']
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.cardAuthorization(sale, customer, card, new Callback<AuthorizationResult>() {

    @Override
    public void onFinish(AuthorizationResult 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.
    }
});

Płatności cykliczne

Płatności cykliczne to po prostu ponowne obciążenia karty (resale) wykonywane w określonych odstępach czasowych. Możesz wykonywać je według schematu określanego przez Twój model biznesowy.

Resale nie wymaga kompletu informacji o karcie i kliencie. Dlatego właśnie potrzebna jest najpierw przynajmniej jedna płatność lub autoryzacja karty.

Oznacza to, że możemy wyróżnić dwa rodzaje płatności cyklicznych:

Płatności cykliczne bazujące na wcześniejszych transakcjach

Zacznij od przygotowania informacji potrzebnych do zrealizowania ponownego obciążenia karty. W tym celu musisz najpierw pozyskać numer ID wcześniejszej transakcji, który jednoznacznie określa daną płatność w systemie PayLane. Możesz łatwo odczytać ten numer podczas przeprowadzania transakcji.

Wprawdzie możesz odwołać się do dowolnej wcześniejszej transakcji dokonanej daną kartą, to jednak zalecamy odnoszenie się do ostatniej dokonanej płatności. Takie podejście ma wiele zalet, pozwala m.in. na łatwe śledzenie przepływu płatności.

Zazwyczaj sprzedawcy przechowują takie numery ID w swoich bazach danych. W ten sposób nie muszą przechowywać żadnych wrażliwych danych, a jednocześnie mają możliwość bezpośredniego odwołania się do wybranej transakcji.

Gdy posiadasz już numer ID (np. pobrany z bazy danych), możesz przygotować dane potrzebne do obciążenia karty i wywołać metodę resaleBySale.

Możesz sprawdzić czy płatność została wykonana prawidłowo wywołując metodę isSuccess. Pozyskanie numeru ID transakcji (lub danych o błędzie, jeśli operacja się nie powiodła) jest również bardzo proste.

Płatności cykliczne są po prostu ponownymi obciążeniami karty. Od Ciebie zależy, czy będą to odstępy tygodniowe, miesięczne, roczne czy inne; podobnie możesz obciążyć kartę np. dopiero, gdy klient osiągnie określoną kwotę do spłaty.

Pobranie ID pierwszej transakcji
PHP/Ruby/Python/Android
$status = $client->cardSale($card_params);
$id_first_sale = $status['id_sale'];
status = client.card_sale(card_params)
id_first_sale = status['id_sale']
status = client.card_sale(card_params)
id_first_sale = status['id_sale']
api.cardSale(sale, customer, card, new Callback<CardSaleResult>() {
    @Override
    public void onFinish(CardSaleResult result) {
        long idFirstSale = result.getIdSale();
    }
});
Realizacja transakcji / resale
PHP/Ruby/Python/Android
$resale_params = array(
    'id_sale'     => $id_first_sale,
    'amount'      => 99.99,
    'currency'    => 'EUR',
    'description' => 'Recurring billing product #1',
);

// perform the resale:
try {
    $status = $client->resaleBySale($resale_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking transaction status example (optional):
if ($client->isSuccess()) {
    echo "Success, second id_sale: {$status['id_sale']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
resale_params = {
    'id_sale'     => id_first_sale,
    'amount'      => 99.99,
    'currency'    => 'EUR',
    'description' => 'Recurring billing product #1'
}

# perform the resale:
begin
    status = client.resale_by_sale(resale_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking transaction status example (optional):
if client.success?
    puts "Success, id_second_sale: #{status["id_sale"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
resale_params = {
  'id_sale'     : id_first_sale,
  'amount'      : 99.99,
  'currency'    : 'EUR',
  'description' : 'Recurring billing product #1'
}

# perform the resale:
try:
    status = client.resale_by_sale(resale_params)
except Exception, e:
    # handle exceptions here

# checking transaction status example (optional):
if client.is_success():
    print 'Success, second id_sale: %s' % status['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.resaleSale(
    idFirstSale, 
    99.99, 
    "EUR", 
    "Recurring billing product #1", 
    new Callback<FraudSaleResult>() {
        @Override
        public void onFinish(FraudSaleResult 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.
        }
    }
);

Płatności cykliczne bazujące na autoryzacji karty

Jeśli klient nie dokonał jeszcze żadnego zakupu (a więc nie było ani jednej płatności), nadal możesz rozpocząć płatności cykliczne. Klient musi tylko podać potrzebne dane karty.

Możesz zebrać wymagane informacje w różnych sytuacjach, np.:

  • kiedy klient zakłada konto w Twoim e-sklepie,
  • gdy klient zapisuje się na bezpłatny okres testowy Twojej usługi.
Gdy zbierzesz te dane, wykonaj autoryzację karty i zapisz jej numer ID.

Wróćmy do płatności cyklicznych. Zakładając, że dysponujesz już numerem ID autoryzacji karty klienta, możesz przygotować dane potrzebne do pobrania środków, czyli wykonania resale’a (a więc wywołania metody resaleByAuthorization).

Podobnie jak w przypadku płatności bazujących na wcześniejszych transakcjach, tak i tu możesz sprawdzić, czy płatność cykliczna się powiodła, wywołując metodę isSuccess. Możesz także odczytać numer ID transakcji lub dane o błędzie, w razie niepowodzenia operacji.

Pobranie id autoryzacji
PHP/Ruby/Python/Android
$id_authorization = $status['id_authorization'];
id_authorization = status["id_authorization"]
id_authorization = status['id_authorization']
AuthorizationResult result =...;
long idAuthorization = result.getIdAuthorization();
Zrealizowanie transakcji w oparciu o wcześniejszą autoryzację
PHP/Ruby/Python/Android
$resale_params = array(
    'id_authorization' => $id_authorization,
    'amount'           => 99.99,
    'currency'         => 'EUR',
    'description'      => 'Recurring billing product #1',
);  

// perform the resale:
try {
    $status = $client->resaleByAuthorization($resale_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking transaction status example (optional):
if ($client->isSuccess()) {
    echo "Success, second id_sale: {$status['id_sale']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
resale_params = {
    'id_authorization' => id_authorization,
    'amount'           => 99.99,
    'currency'         => 'EUR',
    'description'      => 'Recurring billing product #1'
}

# perform the resale:
begin
    status = client.resale_by_authorization(resale_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking transaction status example (optional):
if client.success?
    puts "Success, id_second_sale: #{status["id_sale"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
resale_params = {
  'id_authorization' : id_authorization,
  'amount'           : 99.99,
  'currency'         : 'EUR',
  'description'      : 'Recurring billing product #1'
}

# perform the resale:
try:
    status = client.resale_by_authorization(resale_params)
except Exception, e:
    # handle exceptions here

# checking transaction status example (optional):
if client.is_success():
    print 'Success, second id_sale: %s' % status['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.resaleAuthorization(idAuthorization, 99.99, "EUR", "Recurring billing product #1", new Callback<FraudSaleResult>() {

    @Override
    public void onFinish(FraudSaleResult 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.
    }
});

Zwrot środków

Zdarza się, że chcesz zwrócić klientowi pieniądze (np. oddał lub reklamował towar) – całą kwotę lub tylko jej część. Do tego właśnie służy refund, czyli zwrot środków; pozwala na oddanie określonej kwoty w powiązaniu z konkretną transakcją.

Aby przeprowadzić refund, musisz znać numer ID transakcji, który jednoznacznie identyfikuje daną płatność w systemach PayLane. Możesz łatwo pozyskać ten numer podczas dokonywania transakcji.

Zazwyczaj sprzedawcy zapisują takie numery ID transakcji w swoich bazach danych. Dzięki temu nie muszą sami przechowywać wrażliwych danych i wciąż są w stanie odnieść się do konkretnej płatności.

Przygotuj dane potrzebne do wykonania refunda. Zauważ, że możesz podać kwotę transakcji lub mniejszą; jeśli podasz kwotę większą niż kwota transakcji, system zwróci błąd. Możesz również określić walutę oraz powód zwrotu środków.

Podobnie jak w przypadku każdej innej transakcji, tak i tu możesz sprawdzić czy refund został wykonany prawidłowo wywołując metodę isSuccess. Pozyskanie numeru ID refundu (lub danych o błędzie, jeśli operacja się nie powiodła) jest również bardzo proste i może wyglądać jak na poniższym przykładzie.

Pobranie id transakcji
PHP/Ruby/Python/Android
$status = $client->cardSale($card_params);
$id_sale = $status['id_sale'];
status = client.card_sale(card_params)
id_sale = status['id_sale']
status = client.card_sale(card_params)
id_sale = status['id_sale']
api.cardSale(sale, customer, card, new Callback<CardSaleResult>() {
    @Override
    public void onFinish(CardSaleResult result) {
        long idFirstSale = result.getIdSale();
    }
});
Realizacja refundu
PHP/Ruby/Python/Android
$refund_params = array(
    'id_sale' => $id_sale,
    'amount'  => 9.99,
    'reason'  => 'Partial refund',
);

// perform the refund:
try {
    $status = $client->refund($refund_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking refund status example (optional):
if ($client->isSuccess()) {
    echo "Success, id_refund: {$status['id_refund']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
refund_params = {
    'id_sale' => id_sale,
    'amount'  => 9.99,
    'reason'  => 'Partial refund.'
}

# perform the refund:
begin
    status = client.refund(refund_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking refund status example (optional):
if client.success?
    puts "Success, id_refund: #{status["id_refund"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
refund_params = {
  'id_sale' : id_sale,
  'amount'  : 9.99,
  'reason'  : 'Partial refund'
}

# perform the refund:
try:
    status = client.refund(refund_params)
except Exception, e:
    # handle exceptions here

# checking refund status example (optional):
if client.is_success():
    print 'Success, id_refund: %s' % status['id_refund']
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.refund(idSale, 9.99, "EUR", "Partial refund", new Callback<FraudSaleResult>() {

    @Override
    public void onFinish(FraudSaleResult 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.
    }
});

Płatności single-click / one click

Płatności single-click pozwalają na zakup jednym kliknięciem – klient nie musi podawać żadnych informacji. Usługa wykorzystuje mechanizm resale (podobnie jak płatności cykliczne) pozwalający na pobranie środków z karty.

Możesz dokonywać takich transakcji wykorzystując informacje, które klient podał przy innej okazji, np.:

Single-click bazujące na wcześniejszych transakcjach

Po przeprowadzeniu pierwszej transakcji, możesz łatwo przeprowadzić kolejne wykorzystując informacje podane przy pierwszej płatności. Oznacza to, że nie musisz wymagać podania informacji o karcie i kliencie za każdym razem, co stanowi duże udogodnienie dla Ciebie oraz kupujących.

Jeśli powracający klient zainicjuje transakcję (np. klikając przycisk “Kup” na stronie produktu), możesz wykorzystać informacje z jego wcześniejszej płatności.

Pozyskaj numer ID poprzedniej transakcji. Jeśli przeprowadziłeś ją bezpośrednio przed ponownym pobraniem środków (resale), możesz otrzymać numer ID z informacji zwracanych przez metodę cardSale.

Sprawdzenie statusu transakcji, pobranie numeru ID czy sprawdzenie informacji o ewentualnym błędzie odbywa się w taki sam sposób, jak przy zwykłej płatności.

Zrealizowanie transakcji single click
PHP/Ruby/Python/Android
$resale_params = array(
    'id_sale'     => $id_first_sale,
    'amount'      => 99.99,
    'currency'    => 'EUR',
    'description' => 'Recurring billing product #1',
);  

// perform the resale:
try {
    $status = $client->resaleBySale($resale_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking transaction status example (optional):
if ($client->isSuccess()) {
    echo "Success, second id_sale: {$status['id_sale']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
resale_params = {
    'id_sale'     => id_first_sale,
    'amount'      => 99.99,
    'currency'    => 'EUR',
    'description' => 'Recurring billing product #1'
}

# perform the resale:
begin
    status = client.resale_by_sale(resale_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking transaction status example (optional):
if client.success?
    puts "Success, id_second_sale: #{status["id_sale"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
resale_params = {
  'id_sale'     : id_first_sale,
  'amount'      : 99.99,
  'currency'    : 'EUR',
  'description' : 'Recurring billing product #1'
}

# perform the resale:
try:
    status = client.resale_by_sale(resale_params)
except Exception, e:
    # handle exceptions here

# checking transaction status example (optional):
if client.is_success():
    print 'Success, second id_sale: %s' % status['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.resaleSale(
    idFirstSale, 
    99.99, 
    "EUR", 
    "Recurring billing product #1", 
    new Callback<FraudSaleResult>() {

        @Override
        public void onFinish(FraudSaleResult 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.
        }
    }
);

Single-click bazujące na autoryzacji

Nie musisz wymagać wcześniej zrealizowanej płatności, by udostępnić klientowi usługę single-click. Wystarczy, jesli klient poda informacje potrzebne do jej zrealizowania. Może zrobić to np. podczas zakładania konta w Twoim e-sklepie lub zapisując się do Twojej usługi.

Gdy pozyskasz takie informacje, musisz tylko autoryzować kartę i zapisać otrzymany numer ID.

Dzięki numerowi ID autoryzacji możesz dokonywać dalszych obciążeń karty. Najczęściej sprzedawcy zapisują taki numer w swojej bazie danych – dzięki temu są w stanie używać go w przyszłości do powtórnych obciążeń, a jednocześnie nie muszą operować wrażliwymi danymi.

Teraz przygotuj informacje potrzebne do obciążenia karty (wykonania resale’a). Potrzebujesz jedynie numeru ID autoryzacji oraz informacje opisujące transakcję.

Sprawdzenie statusu transakcji, pobranie numeru ID czy sprawdzenie informacji o ewentualnym błędzie odbywa się w taki sam sposób, jak przy zwykłej płatności.

Pobranie id autoryzacji
PHP/Ruby/Python/Android
$id_authorization = $status['id_authorization'];
id_authorization = status["id_authorization"]
id_authorization = status['id_authorization']
AuthorizationResult result =...;
long idAuthorization = result.getIdAuthorization();
Realizacja single click w oparciu o id autoryzacji
PHP/Ruby/Python/Android
$resale_params = array(
    'id_authorization' => $id_authorization,
    'amount'           => 99.99,
    'currency'         => 'EUR',
    'description'      => 'Recurring billing product #1',
);  

// perform the resale:
try {
    $status = $client->resaleByAuthorization($resale_params);
} catch (Exception $e) {
    // handle exceptions here
}

// checking transaction status example (optional):
if ($client->isSuccess()) {
    echo "Success, second id_sale: {$status['id_sale']} \n";
} else {
    die("Error ID: {$status['error']['id_error']}, \n".
        "Error number: {$status['error']['error_number']}, \n".
        "Error description: {$status['error']['error_description']}");
}
resale_params = {
    'id_authorization' => id_authorization,
    'amount'           => 99.99,
    'currency'         => 'EUR',
    'description'      => 'Recurring billing product #1'
}

# perform the resale:
begin
    status = client.resale_by_authorization(resale_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

# checking transaction status example (optional):
if client.success?
    puts "Success, id_second_sale: #{status["id_sale"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
    exit
end
resale_params = {
  'id_authorization' : id_authorization,
  'amount'           : 99.99,
  'currency'         : 'EUR',
  'description'      : 'Recurring billing product #1'
}

# perform the resale:
try:
    status = client.resale_by_authorization(resale_params)
except Exception, e:
    # handle exceptions here

# checking transaction status example (optional):
if client.is_success():
    print 'Success, second id_sale: %s' % status['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.resaleAuthorization(idAuthorization, 99.99, "EUR", "Recurring billing product #1", new Callback<FraudSaleResult>() {

    @Override
    public void onFinish(FraudSaleResult 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.
    }
});

3-D Secure

Zrealizowanie transakcji 3-D Secure odbywa się w kilku krokach i wymaga wykonania kilku dodatkowych czynności w porównaniu z normalną (niezabezpieczoną mechanizmem 3-D Secure) transakcją płatniczą.

3-D Secure Krok 1: Przygotuj dane

Przygotuj dane potrzebne do przeprowadzenia płatności jak w przykładzie.
Przygotowanie danych do transakcji 3-D Secure
PHP/Ruby/Python/Android
$card_params = array(
    'sale' => array(
        'amount'      => 100.00,
        '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',
        ),
    ),
    'card' => array(
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
     ),
     'back_url' => 'http://example.com/3dsecure', // 3d secure back redirect url
);
card_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'
        }
    },
    'card' => {
        'token' => '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
    },
    'back_url' => 'http://example-url.com'
}
card_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'
    }
  },
  'card' : {
    'token' : '12a34b45c67d89e00f1aa2bb3cc4dd5ee6ff12a34b45c67d89e00f1aa2bb3cc4'
  },
  'back_url' : 'http://example.com/3dsecure'
}
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);
Card card = new Card("4111111111111111", "03", "2017", "John Doe", "123")

3-D Secure Krok 2: Sprawdź kartę

Aby sprawdzić, czy karta jest zapisana do programu 3-D Secure, po prostu wywołaj metodę checkCard3DSecureByToken. Jeśli karta jest zapisana do programu 3-D Secure, otrzymasz numer autoryzacji id_3dsecure_auth.

Jeśli autoryzacja 3-D Secure się powiodła (karta jest zapisana do programu 3-D Secure), możesz przeprowadzić transakcję z użyciem tego mechanizmu i przekierować klienta na stronę dostawcy usługi 3-D Secure (używając adresu otrzymanego w wartości redirect_url).

Jeśli transakcja została odrzucona i został zwrócony błąd, zrealizuj po prostu zwyczajną pojedynczą płatność. Powinieneś również zapisać dane błędu w swoim systemie.

Sprawdzenie czy karta jest w systemie 3-D Secure
PHP/Ruby/Python/Android
try {
    $status = $client->checkCard3DSecureByToken($card_params);
}
catch (Exception $e) {
    // Handle exception here, for example show an error page, stop action
}

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

if (true == $status['is_card_enrolled'])
{
    // redirect to 3-D Secure provider
    header('Location: ' . $status['redirect_url']);
    die;
}
begin
    status = client.check_card_3d_secure_by_token(card_params)
rescue PayLane::ClientError => e
    # handle exceptions here
end 

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

if status['is_card_enrolled']
    # redirect to url in status['redirect_url']
    exit
end
try:
    status = client.check_card_3d_secure_by_token(card_params)
except Exception, e:
    # handle exceptions here

if client.is_success():
    print 'Success, id_3dsecure_auth: %s' % status['id_3dsecure_auth']
else:
    print 'Error (%s), %s - %s' % (status['error'].get('id_error'),
                                   status['error'].get('error_number'),
                                   status['error'].get('error_description'))

if status['is_card_enrolled']:
    # redirect to url in status['redirect_url']
    sys.exit()
api.secure3DCheckCard(sale, customer, card, "http:// example.com/3dsecure", new Callback<Secure3DSaleResult>() {

@Override
public void onFinish(Secure3DSaleResult result) {

    if (result.isEnrolled()) {
        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.
    }
});

3-D Secure Krok 3: Transakcja

Po wprowadzeniu stosownych danych, klient zostanie przekierowany z powrotem na Twoją stronę(back_url). Powinieneś teraz sprawdzić zwrócone informacje, aby uniknąć ewentualnych prób oszustwa i sprawdzić status transakcji.

Jeśli wszystko odbyło się poprawnie, to po tej weryfikacji możesz już przeprowadzić faktyczną płatność opartą na autoryzacji 3-D Secure.

Możesz sprawdzić, czy płatność się powiodła, wywołując metodę isSuccess. Pozyskanie numeru ID transakcji (lub informacji o błędzie, jeśli coś pójdzie nie tak), jest również bardzo proste i może wyglądać jak w przykładzie.

Sprawdzenie danych po powrocie ze strony z zabezpieczeniem 3-D Secure
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_3dsecure_auth
    $id = $_GET['id_3dsecure_auth'];
    
$calc_hash = sha1("{$salt}|{$status}|{$description}|{$amount}|{$currency}|{$id}");

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

// check transaction status
if ($status === 'ERROR') {
    die("Error, 3-D auth transaction declined");
} else {
    // 3-D Secure authorization completed, perform sale
}
# 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_3dsecure_auth']
else
# redirect to an index action to correct the payment + simple notice
# for Rails: redirect_to :index, :notice => "Error, 3-D auth transaction declined"
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, :notice => "Wrong hash"
end


# check transaction status

if status == 'ERROR'
# redirect to an index action to correct the payment + simple notice
# for Rails: redirect_to :index, :notice => "Error, 3-D auth transaction declined"
else
# 3-D Secure authorization completed, perform sale
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_3dsecure_auth = None

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

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

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

# check transaction status
if status == 'ERROR':
    sys.exit('Error, 3-D auth transaction declined')
else:
    # '3-D Secure authorization completed, perform sale'
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 hash = map.get("hash");
                String id = map.get("id_3dsecure_auth");

                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_description");
                    // Error, transaction declined

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

            } 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);
}
Zrealizowanie transakcji po weryfikacji 3-D Secure
PHP/Ruby/Python/Android
try {
    $status = $client->saleBy3DSecureAuthorization(array ('id_3dsecure_auth' => $id));
} catch (Exception $e) {
    // Handle exception here, for example show an error page, stop action
}

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

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

if client.is_success():
    print 'Success, id_sale: %s' % status['id_sale']
else:
    print 'Error (%s), %s - %s' % (status['error'].get('id_error'),
                                   status['error'].get('error_number'),
                                   status['error'].get('error_description'))
Secure3DSaleResult result =...;
long id3dSecureAuth = result.getId3dSecureAuth();
api.secure3DAuthSale(id3dSecureAuth, new Callback<CardSaleResult>() {

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

3-D Secure Krok 3a: Karta niezapisana do 3-D Secure

Sytuacja opisana powyżej jest przypadkiem idealnym. Może się jednak okazać, że karta nie jest zapisana do programu 3-D Secure i wynik autoryzacji 3-D Secure będzie negatywny. Oto jak należy postąpić w takim przypadku.

Po wywołaniu metody mającej zrealizować autoryzację 3-D Secure, po prostu wywołaj metodę saleBy3DSecureAuthorization zupełnie tak, jakbyś zrobił w przypadku, gdyby wynik autoryzacji był pozytywny. To ważne (ze względów bezpieczeństwa), by posłużyć się ta metodą, a nie przeprowadzać w tym momencie zwykłej transakcji kartą – dzięki temu próba przeprowadzenia płatności z użyciem 3-D Secure zostanie zarejestrowana.

Możesz sprawdzić, czy płatność się powiodła, wywołując metodę isSuccess. Pozyskanie numeru ID transakcji (lub informacji o błędzie, jeśli coś pójdzie nie tak), jest również bardzo proste i może wyglądać jak w przykładzie.

Realizacja transakcji gdy karta nie jest w systemie 3-D Secure
PHP/Ruby/Python/Android
$id_3dsecure_auth = $status['id_3dsecure_auth'];

if (true != $status['is_card_enrolled'])
{
    try {
        $status = $client->saleBy3DSecureAuthorization(array ('id_3dsecure_auth' => $id_3dsecure_auth));
    } catch (Exception $e) {
        // Handle exception here, for example show an error page, stop action
    }
}

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

unless status['is_card_enrolled']
    begin
        status = client.sale_by_3d_secure_authorization({"id_3dsecure_auth" => id_3dsecure_auth})
    rescue PayLane::ClientError => e
        # Handle exception here, for example show an error page, stop action
    end 
end

if client.success?
    puts "Success, id_sale: #{status["id_sale"]}"
else
    puts "Error ID: #{status["error"]["id_error"]}, \n"\
         "Error number: #{status["error"]["error_number"]}, \n"\
         "Error description: #{status["error"]["error_description"]}"
end
if not is_card_enrolled:
    try:
        status = client.sale_by_3d_secure_authorization(
            {'id_3dsecure_auth': id_3dsecure_auth})
    except Exception, e:
        # Handle exception here, for example show an error page, stop action

if client.is_success():
    print 'Success, id_sale: %s' % status['id_sale']
else:
    print 'Error (%s), %s - %s' % (status['error'].get('id_error'),
                                   status['error'].get('error_number'),
                                   status['error'].get('error_description'))
Secure3DSaleResult result =...;
if (!result.isEnrolled()) {
    api.secure3DAuthSale(result.getId3dSecureAuth(), new Callback<CardSaleResult>() {

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