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)
allows creation of Recurrent VISA bills (Subscriptions)
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',
# recurrent fields
subscription_id => 'SUBSCRIPTION_ID',
subscription_orderid => 'SUB_ORDERID',
subscription_status => 'SUBSCRIPTION_STATUS',
startdate => 'SUB_STARTDATE',
enddate => 'SUB_ENDDATE',
status => 'SUB_STATUS',
period_unit => 'SUB_PERIOD_UNIT', # 'd', 'ww', 'm' (yes two 'w's) for resp daily weekly monthly
period_moment => 'SUB_PERIOD_MOMENT', # Integer, the moment in time on which the payment is (0-7 when period_unit is ww, 1-31 for d, 1-12 for m?)
period_number => 'SUB_PERIOD_NUMBER',
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_recur = (@args_basic, qw/name subscription_id subscription_orderid invoice_number amount currency startdate enddate period_unit period_moment period_number/, has_cc_number() ? @args_ccard : @args_alias ),
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 authorization 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 => 'authorization 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");
};
example with recurrent transaction using an alias
my $res = new Business::OnlinePayment('Ogone',%auth);
$res->content( action => 'recurrent authorization',
alias => 'wilma_finstone',
name => 'Wilma Flinstone',
cvc => '423',
amount => '42',
description => "monthly subscription to bedrock magazine",
subscription_id => 12312312
invoice_number => 9123,
subscription_orderid => 123112,
startdate => '2012-01-01',
enddate => '2013-01-01',
status => 1,
period_unit => 'm',
period_moment => 1,
period_number => 1,
... );
$res->submit();
);
Optional parameters
Configuration parameters
- sha_type
- flag3d
- win3ds
STATE DIAGRAMS
Authorization Only
Client Ogone HTTPS Bank
------------------------------------------------------------------------
1 +---|Authorization Only| orderID=1----------->. [RES]
|
2 *<---STATUS=5 ---------0000-------------------'
Post Authorization
Client Ogone HTTPS Bank
------------------------------------------------------------------------
1 +---|Post Authorization| orderID=1----------->. [SAL]
|
2 *<---STATUS=91 PAYID=.. PAYIDSUB=.. ----------+ (processing)
|
3 `------->.
| (processed) +$
4 STATUS=9 .<-----------'
- 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
Parse 3d-secure HTML
use SHA1 passwd hashing see: https://secure.ogone.com/ncol/test/hash_pswd.aspi
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.
export OGONE_PSPID=bob
export OGONE_USERID=bob_api
export 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 DirectLink integration guide https://secure.ogone.com/ncol/Ogone_DirectLink_EN.pdf
- 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
Technical information > Your technical settings > Global security parameters
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 371:
Non-ASCII character seen before =encoding in 'Privilège,'. Assuming UTF-8