Direct Debit

Wprowadzenie do Direct Debit

Direct debit to metoda płatności popularna w niektórych krajach europejskich (np. w Niemczech). Łączy pewne cechy kart i przelewów – pobranie środków może być zainicjowane przez sprzedawcę (jak w przypadku kart), natomiast są one pobierane nie z karty, ale z konta (jak w przypadku przelewów).

Wdrożenie płatności Direct Debit

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 - Wdrożenia i testy.

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

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 konta (account).

Teraz po prostu wykonaj płatność używając metody directDebitSale.

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.

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

Zauważ, że transakcje direct debit oznaczane są po wykonaniu jako pending. Możesz sprawdzić ich status (czy zmienił się na performed, cleared itd.) korzystając z metody getSaleInfo.

Przygotowanie danych do transakcji
PHP/Ruby/Python/Android
$ddebit_params = array(
    'sale' => array(
        'amount'      => 19.99,
        'currency'    => 'EUR',
        'description' => 'Product #1'
    ),
    'customer' => array(
        'name'    => 'Hans Muller',
        'email'   => 'hans@muller.de',
        'ip'      => '127.0.0.1',
        'address' => array (
            'street_house' => 'Platz der Republik 1',
            'city'         => 'Berlin',
            'state'        => 'Berlin',
            'zip'          => '11011',
            'country_code' => 'DE',
        ),
    ),
    'account' => array(
        'account_holder'  => 'Hans Muller',
        'account_country' => 'DE',
        'iban'            => 'DE12345678901234567890',
        'bic'             => 'BICBICDE',
        'mandate_id'      => '54321',
    ),
);
ddebit_params = {
    'sale'=> {
        'amount'      => 19.99,
        'currency'    => 'EUR',
        'description' => 'Product #1'
    },
    'customer' => {
        'name'    => 'Hans Muller',
        'email'   => 'hans@muller.de',
        'ip'      => '127.0.0.1',
        'address' => {
            'street_house' => 'Platz der Republik 1',
            'city'         => 'Berlin',
            'state'        => 'Berlin',
            'zip'          => '11011',
            'country_code' => 'DE'
        }
    },
    'account' => {
        'account_holder'  => 'Hans Muller',
        'account_country' => 'DE',
        'iban'            => 'DE12345678901234567890',
        'bic'             => 'BICBICDE',
        'mandate_id'      => '54321'
    }
}
ddebits_params = {
  'sale' : {
    'amount'      : 19.99,
    'currency'    : 'EUR',
    'description' : 'Product #1'
  }
  'customer' : {
    'name'  : 'Hans Muller',
    'email' : 'hans@muller.de',
    'ip'    : '127.0.0.1',
    'address' : {
      'street_house' : 'Platz der Republik 1',
      'city'         : 'Berlin',
      'state'        : 'Berlin',
      'zip'          : '500',
      'country_code' : 'DE'
    },
  },
  'account' : {
    'account_holder'  : 'Hans Muller',
    'account_country' : 'DE',
    'iban'            : 'DE12345678901234567890',
    'bic'             : 'BICBICDE',
    'mandate_id'      : '54321'
  }
}
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);
Account account   = new Account("Hans Muller","de","DE12345678901234567890","BICBICDE","54321");
Realizacja transakcji
PHP/Ruby/Python/Android
try {
    $status = $client->directDebitSale($ddebit_params);
} catch (Exception $e) {
    // handle exceptions here
}

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.direct_debit_sale(ddebit_params)
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"]}"
    exit
end
try:
    status = client.direct_debit_sale(ddebits_params)
except Exception, e:
    # handle exceptions here

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.directDebitSale(sale, customer, account, new Callback<SaleResult>() {

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

Reversal

Podobnie jak w przypadku kart występują chargebacki (obciążenia zwrotne), w płatnościach direct debit występuje mechanizm reversal.

Sprawdzanie statusów transakcji direct debit jest wysoce zalecane – w ten sposób można określić, czy reversal (a więc żądanie zwrotu środków) wystąpił, czy nie. Możesz sprawdzać statusy transakcji ręcznie w Merchant Panelu, ale również automatycznie przez API.

Jeśli chcesz użyć API, by sprawdzić status transakcji direct debit, użyj metody getSaleInfo.

Teraz po prostu sprawdź, czy pole is_reversal w sale_info ma wartość true czy false.

Możesz również użyć mechanizmu powiadomień o transakcjach, by sprawdzać statusy transakcji automatycznie. Dzięki temu nie będziesz musiał wysyłać żadnych żądań do naszych systemów – otrzymasz powiadomienie, gdy tylko transakcja zmieni swój status.

Sample Request
PHP/Ruby/Python/Android
try {
    $sale_info = $client->getSaleInfo(array('id_sale' => $id_sale));
}
catch (Exception $e) {
    // handle exceptions here
}
begin
    status = client.get_sale_info({"id_sale" => id_sale})
rescue PayLane::ClientError => e
    # handle exceptions here
end
try:
    sale_info = client.get_sale_info({'id_sale': id_sale})
except Exception, e:
    # handle exceptions here
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.
    }
});

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ść, do której można się odwołać.

Zacznij od przygotowania informacji potrzebnych do zrealizowania ponownej transakcji direct debit. 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, np. jak w przykładzie.

Na stronie Pojedyncza transakcja znajdziesz więcej informacji na temat dokonywania pojedynczej płatności.

Wprawdzie możesz odwołać się do dowolnej wcześniejszej transakcji direct debit, to jednak zalecamy odnoszenie się do ostatniej 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 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 i może wyglądać jak na poniższym przykładzie.

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

Pobranie ID pierwszej transakcji
PHP/Ruby/Python/Android
$status = $client->directDebitSale($ddebit_params);
$id_first_sale = $status['id_sale'];
status = client.direct_debit_sale(ddebit_params)
id_first_sale = status["id_sale"]
status = client.direct_debit_sale(ddebit_params)
id_first_sale = status['id_sale']
api.directDebitSale(sale, customer, account, new Callback<SaleResult>() {
    @Override
    public void onFinish(SaleResult result) {
        long idSale=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',
);

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

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'
}

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

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'
}

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

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(idSale, 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 np. jak w przykładzie.

Na stronie Pojedyncza transakcja znajdziesz więcej informacji na temat dokonywania pojedynczej płatności.

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.

Po prostu wywołaj metodę refund. 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->directDebitSale($ddebit_params);
$id_sale = $status['id_sale'];
status = client.direct_debit_sale(ddebit_params)
id_sale = status["id_sale"]
status = client.direct_debit_sale(ddebit_params)
id_sale = status['id_sale']
api.directDebitSale(sale, customer, account, new Callback<SaleResult>() {
    @Override
    public void onFinish(SaleResult result) {
        long idSale=result.getIdSale();
    }
});
Realizacja refundu
PHP/Ruby/Python/Android
$refund_params = array(
    'id_sale'  => $id_sale,
    'amount'   => 9.99,
    'reason'   => 'Partial refund',
);

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

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.'
}

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

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'
}

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

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