NAME

Business::OnlinePayment::Ogone - Online payment processing via Ogone

SYNOPSYS

use common::sense;
use Data::Dumper;
use Business::OnlinePayment;

my $tx = new Business::OnlinePayment('Ogone', pspid => 'fred', 
                                              login => 'bedrock_api',
                                              password => 'fl1nst0ne' );

$tx->test_transaction(1); # remove when migrating to production env

$tx->content(
    alias => 'Customer 1',          # store (or use) an alias to the card_number (optional)
    card_number => 4111111111111111,# you can use this number for testing
    expiration => 12/15,            # for testing: like MM/YY, but in the future
    cvc => 432,                     # for testing: anything like /d{3}/
    invoice_number => 54321,        # primary transaction identifier
    amount => 23.4,                 # only 2 decimals after . allowed
    currency => 'EUR',              # optional currency (EUR, USD, GBP, CHF, ...)
    sha_type => 512,                # optional SHA checksum (1, 256, 512)
    sha_key => 'a_secret_key',      # the key with which to salt (required if sha_type is defined)
    address => 'Somestreet 234',    # optional customer address
    city => 'Brussels',             # optional customer city
    zip => 1000                     # optional customer zip code
    country => 'BE',                # optional customer country (iso?)
);

eval    { $tx->submit() };
if ($@) { die 'failed to submit to remote because: '.$@ };

if ( $tx->is_success() ) {
    print 'transaction successful\n';
    print Dumper({
        ogone_ncerror   => $tx->result_code,
        ogone_payid     => $tx->authorization });

} else {
    print 'transaction unsuccessful: remote has raised an error\n';
    print Dumper({
        ogone_ncerrorplus   => $tx->error_message,
        http_post_args      => $tx->http_args,
        ogone_xml_response  => $tx->result_xml,
        ogone_raw_response  => $tx->server_response });
}

DESCRIPTION

This module interfaces Business::OnlinePayment with Ogone, a European Payment Provider. It is based on the Ogone DirectLink API.

Ogone accepted credit cards: American Express, MasterCard, VISA, AIRPLUS, Aurore, Billy, Cofinoga, Diners Club, JCB, Laser, Privilège, Solo, MaestroUK, UATP

Features

  • code is documented and hopefully quite readable

  • prevents submitting your transaction twice

  • accepts EUR and US currencies (defaults to Euro)

  • allows creation of VISA Alias mitigating the need to store them temporary (RES -> SAL)

  • close coupling to native API, easy to crossreference

  • allows Ogone's native operations: RES, REN, DEL, DES, SAL, SAS, RFD, RFS

  • uses SHASign to protect your request's integrity

  • implements SHA-1, SHA-256 and SHA-512 signing

  • allows switching between test and production environments

  • low-fat is_success() check, Ogone::Status has more details

  • no dependency on Ogone::Status if memory is a constraint

PROGRAM FLOW

You have a choice when implementing credit card processing. You can pocess the money transfer in one or more steps. If you choose to go with one step, customers will be billed directly.

action => 'Normal Authorization' (with operation => 'SAL' (default))
action => 'Authorization Only' (with operation => 'RES' (default)) then action => 'Post Authorization' (with operation => 'SAS' (default))

METHODS

new()

Not used directly. Creates a new instance. It's possible to pass credential infomation in the constructor.

my $tx = new Business::OnlinePayment('Ogone', pspid => 'fred', login => 'bedrock_api', password => 'fl1nst0ne');

content()

This method takes a lot of parameters to prepare the transaction to be submitted. Depending on these parameters the payment processor will act on them in different ways, you can consider it a sort of dispatch table. The main actors are action, alias, win3ds.

content() internal parameter mappings

# credentials
login   => 'USERID',
password => 'PSWD',
PSPID   => 'PSPID',

# primary identifier
invoice_number => 'orderID',

# transaction identifiers (action = query)
payid => 'PAYID',
payidsub => 'PAYIDSUB',

# credit card data
card_number => 'CARDNO', 
cvc => 'CVC',
expiration => 'ED',
alias => 'ALIAS',

# financial data
currency => 'Currency',
amount => 'amount',

# Ogone specific arguments
operation => 'Operation',       # REN, DEL, DES, SAL, SAS, RFD, RFS
eci => 'ECI',                   # defaults 7: e-commerce with ssl (9: recurring e-commerce)
accepturl => 'accepturl',
declineurl => 'declineurl',
exceptionurl => 'exceptionurl',
paramplus => 'paramplus',
complus => 'complus',
language => 'LANGUAGE',

# Business::OnlinePayment common
description => 'COM',
name => 'CN',
email => 'EMAIL',
address => 'Owneraddress',
zip => 'OwnerZip',
city => 'ownertown',
country => 'ownercty',
phone => 'ownertelno',

# client authentication (not used directly, only here as valid HTTP POST arg)
SHASign => 'SHASign',           # see sha_key, sha_type

# 3d secure arguments
flag3d => 'FLAG3D',
win3ds => 'win3ds',
http_accept => 'HTTP_ACCEPT',
http_user_agent => 'HTTP_USER_AGENT',

content() required parameters

Depending on what action you are triggering a number of parameters are required. You can use the following pseudo perl code as a reference to what exactly is required depending on the parameters.

my @args_basic  = qw/login password PSPID action/;
my @args_ccard  = qw/card_number expiration cvc/;
my @args_alias  = qw/alias cvc/;
my @args_new    = @args_basic, qw/invoice_number amount currency/, has_cc_number() ? @args_ccard : @args_alias;
my @args_post   = @args_basic, qw/invoice_number/;
my @query       = @args_basic, qw/invoice_number/;

for ($action) {
    qr/authorization only/i      => required_arguments( @args_new ),
    qr/normal authorization/i    => required_arguments( @args_new ),
    qr/post authorization/i      => required_arguments( @args_post ),
    qr/query/i                   => required_arguments( @query ),
}

content() examples

example authorize only request: requests a claim on an amount of money [RES] (and creates an alias)

my %auth = ( pspid => 'fred', login => 'bedrock_api', password => 'fl1nst0ne' );

my $id = $session->get('orderid');
my $res = new Business::OnlinePayment('Ogone', %auth);

$res->content( invoice_number => $id, action => 'authorize only', alias => 'wilma flinstone',  ... );
$res->submit();

if ( $res->is_success() ) {
    $dbh->do('insert into tx_status_log (id,status) values (?,?)',undef,$id,"RES OK");
};

example post authorization request: transfers an amount of money [SAL].

my $sal = new Business::OnlinePayment('Ogone', %auth);

$sal->content( invoice_number => $is, action => 'post authorization', ...);
$res->submit();

if ( $res->is_success() ) {
    $dbh->do('insert into tx_status_log (id,status) values (?,?)',undef,$id,"SAL OK");
};

example with alias (ie: no card number used)

my $res = new Business::OnlinePayment('Ogone',%auth);

$res->content(alias => 'wilma flinstone', cvc => 123, ...);
$res->submit();

if ( $res->is_success() ) {
    $dbh->do('insert into tx_status_log (id,status) values (?,?)',undef,$id,"RES OK");
};

Optional parameters

Configuration parameters

sha_type
flag3d
win3ds

STATE DIAGRAMS

Authorize Only

   Client                                 Ogone HTTPS      Bank
------------------------------------------------------------------------

1    +---|Authorize Only| orderID=1----------->. [RES]        
                                               |              
2    *<---STATUS=5 ----------------------------'                    
1 submit authorize only request usin RES operation with orderID=1. This will reserve the money on the credit card.
2 STATUS=5 indicates the authorization succeeded.

Post Authorize

   Client                                 Ogone HTTPS      Bank
------------------------------------------------------------------------

1    +---|Post Authorize| orderID=1----------->. [SAL] 
                                               |
2    *<---STATUS=91 PAYID=.. PAYIDSUB=.. ------+ (processing)
                                               |
3                                              `----------->.
                                                            | (processed) +$
4                                     STATUS=9 .<-----------'
1 some time later, you wish to receive/transfer the money to you. You issue a post authorize request (defaults to SAL)
2 STATUS=91 indicates the payment is being processed. PAYID and PAYIDSUB are references that identify the current operation on the transaction.
3 Ogone handles processing with your bank
4 money has been put into your account. STATUS is set to 9

Refund

   Client                                 Ogone HTTPS      Bank
------------------------------------------------------------------------

1 .->+---|Query| orderID or PAYID,PAYIDSUB= -->. 
  |                                            |       
  |  .<----------------------------------------'                   
  |  |
2 STATUS == 9   
     |
3    `---|Refund| orderID or PAYID,PAYIDSUB= ->. [RFD]
                                               |
4    *<-- STATUS=81 ---------------------------+ (processing)
                                               |
5                                              `----------->.
                                                            | (processed) -$
6                                     STATUS=8 .<-----------'
1 We want to refund a transaction. To check the transaction is refundable, we must first query it.
2 Refunds are only possible once a transaction is completed (STATUS = 9) (e.g. not while it is processing = 91), thus loop until so.
3 Request refund using orderID or PAYID and PAYISUB to identify refundable operation.
4 STATUS=81 indicates the refund is being processed
5 Ogone handles processing with your bank
6 Money has been taken from your account. STATUS is set to 8

TODO

TESTING

To test this module you will need to set your credentials in the environment. Put the following in a file in your hoe directory e.g. ~/.ogone The password is not the same as the PSPID password, you will need to enter the API users' password.

OGONE_PSPID=bob
OGONE_USERID=bob_api
OGONE_PSWD=foobar

Limit access to the ~/.ogone file

chmod 600 ~/.ogone

Then load this file into your env en perform the testcases:

source ~/.ogone
perl -I lib/ t/*.t

INTERACTIVE SESSION

[fred@triceratops ~/business-online-payment-ogone] $ curl -L http://cpanmin.us | perl - --self-upgrade
[fred@triceratops ~/business-online-payment-ogone] $ cpanm -S Devel::REPL
[fred@triceratops ~/business-online-payment-ogone] $ re.pl

001:0> use lib './lib';
002:0> use Business::OnlinePayment::Ogone;
003:0> my $tx = new Business::OnlinePayment('Ogone', pspid => 'fred', login => 'bedrock_api', password => 'fl1nst0ne');

bless( {
  login => "bedrock_api",
  passwprd => "fl1nst0ne",
  port => 443,
  processor => "Ogone",
  pspid => "fred",
  server => "secure.ogone.com"
}, 'Business::OnlinePayment::Ogone' )

004:0> 

REMOTE SIDE DOCUMENTATION

Backend

Ogone Test Backend https://secure.ogone.com/ncol/test/frame_ogone.asp
Ogone Prod Backend https://secure.ogone.com/ncol/prod/frame_ogone.asp

Online Documentation

Homepage http://www.ogone.com
Ogone Alias Manager Option integration guide https://secure.ogone.com/ncol/Ogone_Alias_EN.pdf
Ogone Status Code list https://secure.ogone.com/ncol/paymentinfos1.asp

Bugs, inacuracies and quircks

Security risk: password encryption

Ogone claims to encrypt your password where in fact in only hashes it using the SHA-1 algorithm. I suppose someone else wrote the accompanying texts, because the url states hash_pwsd.

Ofcourse hashing your password is a good thing. An intruder can only steal the hashed password and use it for the specified service until the password is changed and the hash becomes invalid. The advantage is the intruder could never 'read' your real typed in password should you have used it on other services.

But Ogone has made the hashing straight without a salt, which is a serious issue if you take rainbow tables into account. Using the rainbow table technique on an unsalted string allows an attacker to reverse engineer the password rather quickly.

Login into your test environment and goto https://secure.ogone.com/ncol/test/hash_pswd.asp to verify.

echo -n yourpass | sha1sum

Refunds: not possible right after transaction.

It takes a while to approve your transaction. You will need to wait for its status to drop from 91 to 9. If your account type allows it, it's possible to refund by using another transaction. It was however not tested.

Support Helpdesk

Ogone's support helpdesk will answer promptly within a day, also for test accounts. You will need to contact them if you would like to add some features like for example 3d-secure. Most of my helpdesk request have been resolved in matter of days.

Credentials

You need to create a subaccount to access the API. Use that subaccount as the USERID or login. You will need to login with the password of the api user.

If you try to submit requests with a bad username or password your account can get blocked. To unblock your account you should go into the users panel and reactivate the account.

Configuration Parameters

Configure the Ogone Test Backend or Ogone Prod Backend using the following settings:

Technical information > Your technical settings > Global security parameters

Compose string: Each parameter
Hash algorithm: same as $sha_type
Character encoding: UTF-8

Technical information > Your technical settings > Global security parameters

SHA-IN Pass phrase: same as $sha_key

1 POD Error

The following errors were encountered while parsing the POD:

Around line 58:

Non-ASCII character seen before =encoding in 'Privilège,'. Assuming UTF-8