CREDIT CARD PAYMENTS Document created on 2014-09-23 Nicolas Bondier [pdf][doc][htm] * * Page 1/57 * Copyright © 2016 by Switzernet Contents Introduction ............................................................................................................................................................. 3 Postfinance integration with portabilling................................................................................................................ 3 Postfinance e-payment configuration .................................................................................................................. 3 Direclink activation ............................................................................................................................................. 3 Create an API user .............................................................................................................................................. 3 Allow access for portabilling slave IP address .................................................................................................... 4 Configure return parameters ............................................................................................................................. 5 Porta-billing configuration ................................................................................................................................... 5 Use PostFinance gateway in Ogone payment processing module on slave. ..................................................... 5 Add PostFinance to the Porta-Billing payments systems. .................................................................................. 6 Payments on website .............................................................................................................................................. 6 Main payment page ............................................................................................................................................. 9 PayPal ................................................................................................................................................................. 25 Process PayPal payments ................................................................................................................................. 25 PayPal payment notifications and update payment on billing ........................................................................ 31 PostFinance ........................................................................................................................................................ 42 Process Postfinance payments ......................................................................................................................... 42 Postfinance payment notifications and update payment in portabilling ........................................................ 45 Liens ...........................................................................................................................Error! Bookmark not defined. Page 2/57 Introduction This document describes the configuration and scripts used for setting different payments systems in PortaBilling and on the main website pay.switzernet.com. Postfinance integration with portabilling PostFinance e-payment have a partnership with Ogone for their e-payment gateway. As the billing permit to use Ogone as payment processor, we could set-up the payment gateway for Postfinance using Ogone as processor with some modifications. Postfinance e-payment configuration Direclink activation First, it is required to set-up the Postfinance account e-payment.postfinance.ch. Go to ‘Configuration’ ‘Abonnement’ ‘Vos options’. DirectLink is the method used by the billing for processing the payments. The options showed in the picture bellow must be activated. To activate them, we had to contact Postfinance e-payment and send an order letter. Create an API user Go to ‘Configuration’ ‘Utilisateurs’ and click the add user button. In the form, enter the data of the new user as bellow and choose ‘”API” user’ option. Save and manually choose a password for your new API user. Page 3/57 Allow access for portabilling slave IP address Under "Configuration" "Informations Techniques" "Controles de données et d’origine", fill the section "Contrôles pour PostFinance DirectLink". The IP addresses to authorize are separated with ";". Here I put all the slave portabilling IP addresses. Page 4/57 Configure return parameters Under "Configuration" -> "Informations Techniques" -> "Retour d'information sur la transaction", go to section "DirectLink". Move all parameters to "Seléctionné" as bellow and save. Porta-billing configuration Use PostFinance gateway in Ogone payment processing module on slave. Connect with ssh to the portabilling slave server and edit the file ‘/home/portaadmin/site_lib/Business/OnlinePayment/Ogone.pm’. Under the ‘set_fefaults’ subroutine, we have the connection settings to Ogone. The server ‘secure.ogone.com’ must be replaced with ‘e-payment.postfinance.ch’ as bellow. Page 5/57 sub set_defaults{ my $self = shift; $self->server('e-payment.postfinance.ch'); $self->port('443'); $self->build_subs('test_path'); $self->build_subs('order_number'); $self->test_path('/ncol/test/orderdirect.asp'); $self->path('/ncol/prod/orderdirect.asp'); } Add PostFinance to the Porta-Billing payments systems. In the slave portabilling server, add a new payment system as follow [link]. Under the login field, enter PostFinance PSID followed by the API user id created before. The both logins must be separated with ‘:’ like ‘PSID:API_userid’. The password to fill is the API user password created before. Then, we can set the Payment System for each currencies [link]. Here is an example. Payments on website In order to facilitate customer’s payments, it is possible to pay directly from the main website or directly access the payment page through pay.switzernet.com. The sections bellow describe the main script made for using PayPal and Postfinance on the website. Files and folders There are three new subfolders added to the /public/ directory on Switzernet.com web site. 140819-epay contains the payment page for the customer and 140824-paypal-notification and 140915postfinance-notification contain the notification system for PayPal and PostFinance. Page 6/57 140819-epay/ ├── custom_icon.png ├── images │ ├── menubar.gif │ └── switzernet.gif ├── include │ ├── db.config.php │ ├── geoiploc.php │ ├── languages │ │ ├── get_language.php │ │ ├── language_DE.php │ │ ├── language_EN.php │ │ ├── language_ES.php │ │ ├── language_FR.php │ │ └── language_RU.php │ ├── Mobile-Detect-master │ │ ├── composer.json │ │ ├── composer.lock │ │ ├── examples │ │ │ ├── demo.php │ │ │ └── session_example.php │ │ ├── export │ │ │ └── exportToJSON.php │ │ ├── LICENSE.txt │ │ ├── Mobile_Detect.json │ │ ├── Mobile_Detect.php │ │ ├── namespaced │ │ │ └── Detection │ │ │ └── MobileDetect.php │ │ ├── README.md │ │ └── tests │ │ ├── BasicsTest.php │ │ ├── bootstrap.php │ │ ├── phpunit.xml │ │ ├── UA_List.inc.php │ │ ├── UA_List.pending.txt │ │ ├── ualist.json │ │ ├── UserAgentTest.php │ │ └── VendorsTest.php │ ├── pay.config.php │ ├── paypal.class.php │ └── paypal.config.php ├── index.php ├── js │ ├── parsley.min.js │ └── parsley_locales │ ├── de.js │ ├── en.js │ ├── es.js │ ├── fr.js Page 7/57 │ ├── ├── ├── ├── └── └── ru.js paypalProcess.php postfinanceProcess.php postfinanceTemplate.css postfinanceTemplate.php style ├── ie.css ├── mobile.css ├── parsley.css ├── style.css └── style2.css 140824-paypal-notification/ ├── live.php ├── PortaBillingSoapClient.php └── sandbox.php 140915-postfinance-notification ├── index.php └── PortaBillingSoapClient.php Page 8/57 Main payment page [index.php] CODE COMMENT <? if(!isset($_SESSION)){session_start();} Session start include_once "include/pay.config.php"; Including general configuration file. Getting information posted to this page. if ( isset($_GET['amount']) ){ if ( preg_match('/^[0-9]+(\.[0-9]{0,2})?$/', $_GET['amount']) ){ $price = $_GET['amount']; } } if ( isset($_POST['amount']) ){ if ( preg_match('/^[0-9]+(\.[0-9]{0,2})?$/', $_POST['amount']) ){ $price = $_POST['amount']; } } $number = ""; if ( isset( $_POST['action'] ) ){ $_SESSION['action'] = $_POST['action']; } if ( isset( $_GET['action'] ) ){ $_SESSION['action'] = $_GET['action']; } $debug_IE = TRUE; $using_ie6 = (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 6.') !== FALSE); Page 9/57 Debug for displaying on old web browsers ?> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=device-width, initialscale=1,maximum-scale=1.0"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <link rel="apple-touch-icon" href="custom_icon.png"> <meta name="format-detection" content="telephone=no"> <?php if ( $debug_IE ) { ?> <meta http-equiv="X-UA-Compatible" content="IE=6" /> <?php } else { ?> <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" /> <?php } ?> <meta name="author" content="Nicolas Bondier - Switzernet"> <link rel="stylesheet" href="../070608-subscribe/pure-min.css"> <link href="../060801-web/style.css" rel="stylesheet" type="text/css"> <link href="style/style2.css" rel="stylesheet" type="text/css"> <link href="style/mobile.css" rel="stylesheet" type="text/css"> <script type="text/javascript" src="../070608-subscribe/jquery1.7.2.min.js"></script> <script type="text/javascript" src="js/parsley.min.js" ></script> <link href="style/parsley.css" rel="stylesheet" type="text/css"> <?php if ( isset( $_SESSION['language']['code'] ) && file_exists( "js/parsley_locales/".strtolower($_SESSION['language']['code']).".js") ) { ?> <script src="js/parsley_locales/<?= strtolower($_SESSION["language"]["code"]) ?>.js"></script> <script type="text/javascript"> window.ParsleyValidator.setLocale('<?= strtolower($_SESSION["language"]["code"]) ?>'); </script> <?php Page 10/57 HTML begins here. The head content contains information for better displaying on mobile applications and old browser. Getting the localization files (language) for error handling for the customer. } ?> <script type="text/javascript"> function changeLanguage(selectLanguage){ var lang = selectLanguage.value; var form = document.getElementById("formLanguage"); form.submit(); } function changeCurrency(selectCurrency){ var value = selectCurrency.options[selectCurrency.selectedIndex].value; window.location.href = '?currency_code='+value+''; } function getParentElementByTagName( element , tag ){ while( element.tagName.toLowerCase() != tag.toLowerCase() ){ element = element.parentNode; if (!element.tagName) { return; } } return element; } </script> <!--[if lte IE 8]> <link rel="stylesheet" href="style/ie.css" type="text/css" media="screen,projection" /> <![endif]--> <script type="text/javascript"> function initFormAction(){ var select = document.getElementById("paymentType"); if ( select !== null ){ updateFormAction(select); } } Page 11/57 Some JavaScript functions for submitting language and currency changes. A function to simplify the selection of a parent element. Special CSS style sheet for internet explorer. Function to update the “action” parameter of the form after having choose the payment method. It changes the “action” parameter to the function updateFormAction(select){ var form=document.getElementById("payForm"); var option = select.options[select.selectedIndex].value; var ts = "ts="+Date.now(); if ( option == "creditcard" ){ form.action="postfinanceProcess.php?"+ts; } else { form.action="paypalProcess.php?"+ts; } } </script> PostFinance processing script or PayPal processing script. Piwik script for getting statistics. <!-- Piwik --> <script type="text/javascript"> var _paq = _paq || []; _paq.push(["trackPageView"]); _paq.push(["enableLinkTracking"]); (function() { var u=(("https:" == document.location.protocol) ? "https" : "http") + "://switzernet.com/3/public/120427-site-stats/"; _paq.push(["setTrackerUrl", u+"piwik.php"]); _paq.push(["setSiteId", "1"]); var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript"; g.defer=true; g.async=true; g.src=u+"piwik.js"; s.parentNode.insertBefore(g,s); })(); </script> <!-- End Piwik Code --> <title><?= TITLE_PAYMENT_SWITZERNET ?></title> </head> <body style="" onload="initFormAction()"> <div id="container"> <div id="head" style="text-align: center;"> <table style=""> <tbody> <tr> Page 12/57 Starting of the body and the page header. <td align="left"> <a href="http://www.switzernet.com/"> <img style="" class="pure-img" src="../060801web/images/switzernet.gif" alt="Switzernet VoIP téléphonie IP Suisse"></a> <img id="calltheworld" class="pure-img" style="margin:10px" src="../060801-web/images/slogan_6.gif" alt="appeler le monde pour presque rien"> </td> <td style="vertical-align: top;" align="right"> <div class="pure-form pure-form-aligned"> <form id="formLanguage" action="#" method="get"> <fieldset> <select name="language" id="language_select" onchange="changeLanguage(this)"> <?php foreach ($LANGUAGES as $ISO => $text) { if ( $_SESSION['language']['code'] == $ISO ){ echo "<option value='".$ISO."' selected='selected'>".$text."</option>"; } else { echo "<option value='".$ISO."'>".$text."</option>"; } } ?> </select> </fieldset> </form> Language selection End of page header </div> </td> </tr> </tbody> </table> </div> Page 13/57 Starting of the man content div. <div id="pay_div" style=""> <?php if ( $_SERVER['REMOTE_ADDR'] == '46.14.170.24' && FALSE ){ echo time(); } if ( isset( $_SESSION['action'] ) && $_SESSION['action'] == 'cancel' ) { ?> <h1 style="text-align:center;"><?= H1_PAYMENT_CANCELED ?></h1> <table> <tr> <td><?= TEXT_PAYMENT_CANCELED_1 ?></td> </tr> <tr style="text-align:center;"> <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> </table> <?php } elseif ( isset( $_SESSION['action'] ) && $_SESSION['action'] == 'paymentok' ){ ?> <h1 style="text-align:center;"><?= H1_PAYMENT_OK ?></h1> <table> <tr> <td><?= TEXT_PAYMENT_OK_1 ?><?= $_SESSION['ItemNumber'] ?><?= TEXT_PAYMENT_OK_2 ?></td> </tr> <tr style="text-align:center;"> <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> Page 14/57 When the action received is cancel (from PayPal or PostFinance), we display the corresponding content. When the action received is paymentok (from PayPal or PostFinance), we display the corresponding content. </table> <?php } elseif ( isset( $_SESSION['action'] ) && $_SESSION['action'] == 'paymentpending' ){ ?> <h1 style="text-align:center;"><?= H1_PAYMENT_PENDING ?></h1> <table> <tr> <td><?= TEXT_PAYMENT_PENDING_1 ?></td> </tr> <tr style="text-align:center;"> <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> </table> <?php } elseif ( isset( $_SESSION['action'] ) && $_SESSION['action'] == 'paymentfailed' ){ ?> <h1 style="text-align:center;"><?= H1_PAYMENT_FAILED ?></h1> <table> <tr> <td><?= TEXT_PAYMENT_FAILED_1 ?></td> </tr> When the action received is paymentpending (from PayPal), we display the corresponding content. When the action received is paymentfailed (from PayPal or Postfinance), we display the corresponding content. <tr style="text-align:center;"> <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> </table> <?php } elseif ( isset( $_SESSION['action'] ) && $_SESSION['action'] == 'paymenterror' ){ ?> <h1 style="text-align:center;"><?= H1_PAYMENT_FAILED ?></h1> Page 15/57 Error during the payment (from PayPal), we display the corresponding content. <table> <tr> <td><?= TEXT_PAYMENT_FAILED_1 ?></td> </tr> <tr style="text-align:center;"> <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> </table> <?php } elseif ( ( isset( $_SESSION['action'] ) && $_SESSION['action'] == "payment" ) ){ $number = ""; if ( isset( $_SESSION["postdata"]["num_part_1"] ) && isset( $_SESSION["postdata"]["num_part_2"] ) ){ $_SESSION['accountToCredit'] = $_SESSION["postdata"]["num_part_1"]."".$_SESSION["postdata"]["num_part_2"]; unset($_SESSION["postdata"]["num_part_1"]); unset($_SESSION["postdata"]["num_part_2"]); } if ( isset( $_SESSION["postdata"]['number'] ) ){ $_SESSION['accountToCredit'] = $_SESSION["postdata"]['number']; unset($_SESSION["postdata"]["number"]); } $number = $_SESSION['accountToCredit']; $number = preg_replace('/[^0-9,]|,[0-9]*$/','',$number); $number = preg_replace('/^0([0-9]{9})$/', "41$1", $number); if ( ! preg_match('/^41[0-9]{9}$/', $number) ){ ?> <h1 style="text-align:center;"><?= H1_PAYMENT ?></h1> <table> <tr> <td><?= TEXT_INVALID_NUMBER_1 ?><?= $number ?><?= TEXT_INVALID_NUMBER_2 ?></td> </tr> <tr style="text-align:center;"> Page 16/57 Text when received an action called payment. It refers to the last page before submitting the payment to PostFinance or PayPal. We get the numbers from different post forms and test if it match a valid number format (41XXXXXXXXX). If the number format is wring we inform with a text. <td><br><button class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button></td> </tr> </table> <?php } else { $accountFound = FALSE; $currency = 'CHF'; $connection2=mysql_connect(DB2_SERVER.':'.DB2_SERVER_PORT,DB2_SERVER_U SERNAME, DB2_SERVER_PASSWORD) or die(mysql_error().' : erreur de connexion a la base de donnee !'); If the format is correct, we get the account currency to display it on the web page. mysql_select_db(DB2_DATABASE) or die("erreur de connexion a la base de donnees"); $sql = "SELECT iso_4217 FROM Accounts WHERE id = '".$number."' LIMIT 1"; $result = mysql_query($sql,$connection2); if ( mysql_num_rows($result) == 1 ){ $accountFound = TRUE; $value = mysql_fetch_object($result); if ( isset( $currencyNotations[$value->iso_4217] ) ){ $currency = $value->iso_4217; } else { $currency = 'CHF'; } $_SESSION['currency_code'] = $currency; } else { include("include/geoiploc.php"); $ip = $_SERVER["REMOTE_ADDR"]; if ( getCountryFromIP($ip) == 'CH' ){ $_SESSION['currency_code'] = 'CHF'; } else { $_SESSION['currency_code'] = 'EUR'; } } mysql_close($connection2); Page 17/57 If we find the currency, then we set in session. Else, it means no account have been found, we try to find it with the IP address of the customer. The library used for locating the country of the IP can be found here $_SESSION['phone_number'] = $number; http://chir.ag/projects/geoi ploc if ( $_SESSION['currency_code'] != '' ){ $currency = $_SESSION['currency_code']; } ?> <h1 style="text-align:center;"><?= H1_PAYMENT ?></h1> <form data-parsley-validate class="pure-form" method="post" action="paypalProcess.php" id="payForm"><br> <table> <tr> <td class="label" width="40%"> <label><?= TEXT_SIP_ACCOUNT ?></label> </td> <td> <?= $number ?> </td> </tr> <tr class="parsleyError"> <td></td> </tr> <tr> <td class="label"> <label for="amountInput"><?= TEXT_AMOUNT ?> <span style="color: #777;font-size:12px;white-space:nowrap;">(min. 5 <?= $currency ?>)</span></label> </td> <td class="td_input"> <?php if ( $accountFound ){ ?> <?php if ( $layoutType != 'classic' ) { ?> <input class="input_text" data-parsley-errorscontainer="#amountError" name="itemprice" style="width:80px;textalign:left;" min="5" size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } else { ?> <input id="amountInput" class="input_text" dataparsley-errors-container="#amountError" data-parsley-type="number" name="itemprice" style="text-align:left;" min="5" type="text" Page 18/57 Here we display the final payment page before the processor. It permits to select the The final form for selecting the payment processor and amount. size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } ?> <span style="color: #777;font-size:14px;whitespace:nowrap;">&nbsp;&nbsp;<?= $currency ?></span> <?php } else { ?> <?php if ( $layoutType != 'classic' ) { ?> <input class="input_text" data-parsley-errorscontainer="#amountError" name="itemprice" style="width:50px;textalign:left;" min="5" size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } else { ?> <input id="amountInput" class="input_text" dataparsley-errors-container="#amountError" data-parsley-type="number" name="itemprice" style="text-align:left;" min="5" type="text" size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } ?>&nbsp; <select data-parsley-errorscontainer="#amountError" name="currency" onchange="changeCurrency(this)" changeCurrency> <?php foreach ($currencyNotations as $key => $value) { $selected = ""; if ( $_SESSION['currency_code'] == $key ){ $selected = ' selected="selected" ';} echo "<option value='".$key."' ".$selected.">".$key."</option>"; } ?> </select> <?php } ?> </td> </tr> <tr class="parsleyError"> <td id="amountError" colspan="2"></td> </tr> <tr> <?php if ( $currency == 'CHF' ){ ?> <td class="label"> Page 19/57 <label for="paymentType"><?= TEXT_PAIEMENT_METHOD ?></label> </td> <td> <select id="paymentType" onchange="updateFormAction(this)" name="paiementType"> <option value="creditcard" selected="selected"><?= TEXT_CREDIT_CARD ?></option> <option value="paypal" ><?= TEXT_PAYPAL ?></option> <select> </td> <?php } else { ?> <td> <input type="hidden" name="paiementType" value="paypal" /> </td> <?php } ?> </tr> <tr> <td colspan="3" style="text-align:center;"><br> <button type='button' class="pure-button" onclick="window.location.href='<?= $WEBURL ?>?action=number'"><?= BUTTON_BACK ?></button> <input type='submit' name="submitbutt" class="purebutton pure-button-primary" value="<?= BUTTON_PAY ?>"/> </td> </tr> </table> <input type="hidden" name="number" value="<?= $number ?>" /> </form> <?php } } else { ?> <h1 style="text-align:center;"><?= H1_PAYMENT ?></h1> <form data-parsley-validate class="pure-form" method="post" action="#"><br> Page 20/57 If action parameter is not defined or is not in the list of action triggered before, we display the input fields <table> <tr> <td class="label"> <label for="num_part_1"><?= TEXT_SIP_ACCOUNT ?></label> </td> <td class="td_input" > <select data-parsley-errors-container="#numberError1" name="num_part_1" id="num_part_1" required> <option value="" >---</option> <?php foreach ($pref1_pref2 as $id_pref => $prefixes){ if ( $prefixes[0]==$part1 && $prefixes[1]==$part2 && $found=="" ){ $found=$id_pref; } } if ($found==""){ foreach ($pref1_pref2 as $id_pref => $prefixes){ if ($prefixes[0]==$part1){ $found=$id_pref; } } } foreach ($pref1_pref2 as $id_pref => $prefixes){ echo '<option value="'.$prefixes[0].'-'.$prefixes[1].'" '; if ( $id_pref==$found ) { echo "selected = \"selected\""; } echo '>'.$prefixes[0].'-'.$prefixes[1].'</option>'."\n"; } ?> </select> </td> <td class="td_input" > <?php if ( $layoutType != 'classic' ) { ?> <input class="input_text" data-parsley-errorscontainer="#numberError2" style="width:55px;" name="num_part_2" Page 21/57 for selecting the account to credit. Selection of the prefixes. Selection of the last 4 digits. type="number" data-parsley-length="[4, 4]" size="4" placeholder="XXXX" value="<?= $part3 ?>" required/> <?php } else { ?> <input class="input_text" data-parsley-errorscontainer="#numberError2" data-parsley-type="number" data-parsleylength="[4, 4]" name="num_part_2" type="text" maxlength="4" size="5" placeholder="XXXX" value="<?= $part3 ?>" required/> <?php } ?> </td> </tr> This is some hidden cells for displaying error in input cells. <tr class="parsleyError"> <td></td> <td id="numberError1"></td> <td id="numberError2"></td> </tr> <tr> <td colspan="3" style="text-align:center;"><br> <input type="submit" name="submitbutt" class="purebutton pure-button-primary" value="<?= BUTTON_CONTINUE ?>"/> </td> </tr> </table> <input type="hidden" name="action" value="payment" /> <input type="hidden" name="itemname" value="Account crediting" /> <input type="hidden" name="itemQty" value="1" /> <input type="hidden" name="itemdesc" value="Credit your Switzernet account with credit card or paypal." /> </form> <?php } ?> </div> <?php if ( $layoutType != 'classic' ){ ?> <footer> <?php } else { ?> <div id="footer_div"> Page 22/57 Button for validating the form and continuing to last page. Footer with logos of the available payment methods. <?php } ?> <center> <?php if ( $layoutType != 'classic' ){ echo '<hr style="color: #eee;border: 0;width: 90%;height: 3px;background-color: #eee;">'; } else { ?> <br> <br> <br> <?php } ?> <?php if ( $_SESSION['currency_code'] == 'CHF' || $_SESSION['action'] != 'payment' ){ ?> <a href="https://www.postfinance.ch/pf/content/fr.html" target="_blank"><img height="30" src="/public/060801web/images/logoPoste.gif" border="0"></a>&nbsp;&nbsp; <a href="http://www.mastercard.com/" target="_blank"><img height="30" src='/public/060801-web/images/mastercard.gif' border="0"></a>&nbsp;&nbsp; <a href="http://www.visa.com/" target="_blank"><img height="30" src='/public/060801-web/images/visa.gif' border="0"></a>&nbsp;&nbsp; <a href="https://www.paypal.com/fr/webapps/mpp/paypal-popup" title="PayPal Comment Ca Marche" onclick="javascript:window.open('https://www.paypal.com/fr/webapps/mpp /paypal-popup','WIPaypal','toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=1060, height=700'); return false;"><img height="30" src="https://www.paypalobjects.com/webstatic/mktg/logo/pp_cc_mark_37x2 3.jpg" border="0" alt="PayPal Logo" /></a> <?php } else { ?> <a href="https://www.paypal.com/fr/webapps/mpp/paypal-popup" title="PayPal Comment Ca Marche" onclick="javascript:window.open('https://www.paypal.com/fr/webapps/mpp /paypal-popup','WIPaypal','toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes, width=1060, height=700'); return false;"><img style="width:290px;" src="https://www.paypalobjects.com/webstatic/mktg/logocenter/logo_paypal_moyens_paiement_fr.jpg" border="0" alt="PayPal Acceptance Mark" /></a> <?php } ?> Page 23/57 </center> <?php if ( $layoutType != 'classic' ){ ?> </footer> <?php } else { ?> </div> <?php } ?> </div> </body> </html> End of the html Function for getting the prefixes from <?php function pref1_pref2(){ $connection = mysql_connect(DB1_SERVER,DB1_SERVER_USERNAME,DB1_SERVER_PASSWORD, TRUE); mysql_select_db(DB1_DATABASE, $connection); $prefs=array(); $sql = "SELECT id_prefix, prefix1, prefix2 FROM prefixes"; $req = mysql_query($sql) or die('Erreur SQL !<br>'.$sql.'<br>'.mysql_error()); while($prefix = mysql_fetch_assoc($req)){ $prefs[$prefix["id_prefix"]] = array(ereg_replace("^41","0",$prefix["prefix1"]),$prefix["prefix2"]); } mysql_close($connection); return $prefs; } function layoutTypes(){ return array('classic', 'mobile', 'tablet'); } function initLayoutType(){ // Safety check. if (!class_exists('Mobile_Detect')) { return 'classic'; } $detect = new Mobile_Detect; $isMobile = $detect->isMobile(); $isTablet = $detect->isTablet(); Page 24/57 Function for detecting mobile devices. This is required for all displaying differences added to the html and CSS. $layoutTypes = layoutTypes(); // Set the layout type. if ( isset($_GET['layoutType']) ) { $layoutType = $_GET['layoutType']; } else { if (empty($_SESSION['layoutType'])) { $layoutType = ($isMobile ? ($isTablet ? 'tablet' : 'mobile') : 'classic'); } else { $layoutType = $_SESSION['layoutType']; } } // Fallback. If everything fails choose classic layout. if ( !in_array($layoutType, $layoutTypes) ) { $layoutType = 'classic'; } // Store the layout type for future use. $_SESSION['layoutType'] = $layoutType; return $layoutType; } ?> PayPal Process PayPal payments [paypalProcess.php] CODE COMMENT <?php Starting session and including configuration files and a PayPal class for if(!isset($_SESSION)){session_start();} Page 25/57 processing the payment easier. include_once("include/pay.config.php"); include_once("include/paypal.config.php"); include_once("include/paypal.class.php"); $paypalmode = ($PayPalMode=='sandbox') ? '.sandbox' : ''; $PaypalLocalCode = 'CH'; if ( isset( $defaultLocale[$PayPalCurrencyCode] ) ){ $PaypalLocalCode = $defaultLocale[$PayPalCurrencyCode]; } if($_POST) //Post Data received from product list page. { $number = $_POST['number']; if ( ! preg_match('/^41[0-9]{9}$/', $number) ){ die ("wrong number format $number"); } $credit = ""; if ( isset($_POST['itemprice']) ){ $credit = str_replace(',', '.', $_POST['itemprice']); if ( ! preg_match('/^[0-9]+(\.[0-9]{0,2})?$/', $credit ) ){ die ("wrong amount format $credit"); } } Page 26/57 Defining the string for the PayPal URL to reach in case of we are using the sandbox account or the live account. The PayPal variable to define the localization to display. The available localization codes are in the paypal.config.php file. I data has been submitted to this page with POST. We get the VOIP number to credit and verify the format. We get the amount the customer wants to credit on his account. $ItemName = "Credit compte"; //Item Name $ItemPrice = $credit; //Item Price $ItemNumber = $number; //Item Number $ItemDesc = "Switzernet account crediting with credit card or paypal."; //Item Number $ItemCustomDesc = "SIP_account:".$number; $ItemQty = 1; // Item Quantity $ItemTotalPrice = ($ItemPrice*$ItemQty); //(Item Price x Quantity = Total) Get total amount of product; //Grand total including all tax, insurance, shipping cost and discount $GrandTotal = ($ItemTotalPrice + $TotalTaxAmount + $HandalingCost + $InsuranceCost + $ShippinCost + $ShippinDiscount); //Parameters for SetExpressCheckout, which will be sent to PayPal $padata = '&METHOD=SetExpressCheckout'. '&RETURNURL='.urlencode($PayPalReturnURL). '&CANCELURL='.urlencode($PayPalCancelURL). '&PAYMENTREQUEST_0_PAYMENTACTION='.urlencode("SALE"). '&L_PAYMENTREQUEST_0_NAME0='.urlencode($ItemName). '&L_PAYMENTREQUEST_0_NUMBER0='.urlencode($ItemNumber). '&L_PAYMENTREQUEST_0_DESC0='.urlencode($ItemDesc). '&L_PAYMENTREQUEST_0_AMT0='.urlencode($ItemPrice). '&NOSHIPPING=1'. //set 1 to hide buyer's shipping address, in-case products that does not require shipping '&PAYMENTREQUEST_0_ITEMAMT='.urlencode($ItemTotalPrice). '&PAYMENTREQUEST_0_TAXAMT='.urlencode($TotalTaxAmount). '&PAYMENTREQUEST_0_SHIPPINGAMT='.urlencode($ShippinCost). '&PAYMENTREQUEST_0_HANDLINGAMT='.urlencode($HandalingCost). '&PAYMENTREQUEST_0_SHIPDISCAMT='.urlencode($ShippinDiscount). '&PAYMENTREQUEST_0_INSURANCEAMT='.urlencode($InsuranceCost). '&PAYMENTREQUEST_0_CUSTOM='.urlencode($ItemCustomDesc). '&PAYMENTREQUEST_0_AMT='.urlencode($GrandTotal). '&PAYMENTREQUEST_0_CURRENCYCODE='.urlencode($PayPalCurrencyCode). Page 27/57 Main parameters we have to set for account crediting. This is the part for preparing the SetExpressCheckout method. A full documentation of the parameters can be found on PayPal developer website [link]. '&LOCALECODE='.$PaypalLocalCode . //PayPal pages to match the language on your website. '&LOGOIMG=https://slave.switzernet.com:8444/brandpane/logo-switzernet-com2.png'. //site logo '&LANDINGPAGE=Billing'. //Show crdit card '&SOLUTIONTYPE=Sole'. //Show crdit card '&USERSELECTEDFUNDINGSOURCE=CreditCard'. '&CARTBORDERCOLOR=6699FF'. //border color of cart '&ALLOWNOTE=0'; ############# set session variable we need later for "DoExpressCheckoutPayment" ####### $_SESSION['ItemName'] = $ItemName; //Item Name $_SESSION['ItemPrice'] = $ItemPrice; //Item Price $_SESSION['ItemNumber'] = $ItemNumber; //Item Number $_SESSION['ItemDesc'] = $ItemDesc; //Item Description $_SESSION['ItemCustomDesc'] = $ItemCustomDesc; // Item Customer Description $_SESSION['ItemQty'] = $ItemQty; // Item Quantity $_SESSION['ItemTotalPrice'] = $ItemTotalPrice; $_SESSION['TotalTaxAmount'] = $TotalTaxAmount; $_SESSION['HandalingCost'] = $HandalingCost; $_SESSION['InsuranceCost'] = $InsuranceCost; $_SESSION['ShippinDiscount'] = $ShippinDiscount; $_SESSION['ShippinCost'] = $ShippinCost; $_SESSION['GrandTotal'] = $GrandTotal; In the same time, we save the data in the session. Executing the SetExpressCheckOut method to obtain the PayPal token. If the PayPal answer is if("SUCCESS" == strtoupper($httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" success, we redirect the == strtoupper($httpParsedResponseAr["ACK"])) { $paypalurl ='https://www'.$paypalmode.'.paypal.com/cgi-bin/webscr?cmd=_express- user to PayPal with the checkout&token='.$httpParsedResponseAr["TOKEN"].''; received token in the URL. $paypal= new MyPayPal(); $httpParsedResponseAr = $paypal->PPHttpPost('SetExpressCheckout', $padata, $PayPalApiUsername, $PayPalApiPassword, $PayPalApiSignature, $PayPalMode); header('Location: '.$paypalurl); Page 28/57 Else we show the error message. } else { echo '<div style="color:red"><b>Error : </b>'.urldecode($httpParsedResponseAr["L_LONGMESSAGE0"]).'</div>'; echo '<pre>'; //print_r($httpParsedResponseAr); echo '</pre>'; } } When the user confirmed the payment, it used the ReturnURL we set in the SetExpressCheckout method witch is the same page. We receive a token and a Payer ID of the DoExpressCheckout method. if(isset($_GET["token"]) && isset($_GET["PayerID"])) $token = $_GET["token"]; $payer_id = $_GET["PayerID"]; //get session variables $ItemName = $_SESSION['ItemName']; $ItemPrice = $_SESSION['ItemPrice'] ; $ItemNumber = $_SESSION['ItemNumber']; $ItemDesc = $_SESSION['ItemDesc']; $ItemCustomDesc = $_SESSION['ItemCustomDesc']; $ItemQty = $_SESSION['ItemQty']; $ItemTotalPrice = $_SESSION['ItemTotalPrice']; $TotalTaxAmount = $_SESSION['TotalTaxAmount']; $HandalingCost = $_SESSION['HandalingCost']; $InsuranceCost = $_SESSION['InsuranceCost']; $ShippinDiscount = $_SESSION['ShippinDiscount']; $ShippinCost = $_SESSION['ShippinCost']; $GrandTotal $_SESSION['GrandTotal']; $padata = '&TOKEN='.urlencode($token). '&PAYERID='.urlencode($payer_id). '&PAYMENTREQUEST_0_PAYMENTACTION='.urlencode("SALE"). '&L_PAYMENTREQUEST_0_NAME0='.urlencode($ItemName). '&L_PAYMENTREQUEST_0_NUMBER0='.urlencode($ItemNumber). '&L_PAYMENTREQUEST_0_DESC0='.urlencode($ItemDesc). '&L_PAYMENTREQUEST_0_AMT0='.urlencode($ItemPrice). '&PAYMENTREQUEST_0_CUSTOM='.urlencode($ItemCustomDesc). '&PAYMENTREQUEST_0_ITEMAMT='.urlencode($ItemTotalPrice). Page 29/57 We haven’t received the payment yet. = We also get all the session variables. Preparing the data for DoExpressCheckout method. '&PAYMENTREQUEST_0_TAXAMT='.urlencode($TotalTaxAmount). '&PAYMENTREQUEST_0_SHIPPINGAMT='.urlencode($ShippinCost). '&PAYMENTREQUEST_0_HANDLINGAMT='.urlencode($HandalingCost). '&PAYMENTREQUEST_0_SHIPDISCAMT='.urlencode($ShippinDiscount). '&PAYMENTREQUEST_0_INSURANCEAMT='.urlencode($InsuranceCost). '&PAYMENTREQUEST_0_AMT='.urlencode($GrandTotal). '&PAYMENTREQUEST_0_CURRENCYCODE='.urlencode($PayPalCurrencyCode); //We need to execute the "DoExpressCheckoutPayment" at this point to Receive payment from user. $paypal= new MyPayPal(); $httpParsedResponseAr = $paypal->PPHttpPost('DoExpressCheckoutPayment', $padata, $PayPalApiUsername, $PayPalApiPassword, $PayPalApiSignature, $PayPalMode); //Check if everything went ok.. if ( "SUCCESS" == strtoupper($httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($httpParsedResponseAr["ACK"])) { if('Completed' == $httpParsedResponseAr["PAYMENTINFO_0_PAYMENTSTATUS"]) { header('Location: '. $ReturnURL ."?action=paymentok&tid=". $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } elseif('Pending' == $httpParsedResponseAr["PAYMENTINFO_0_PAYMENTSTATUS"]) { header('Location: '. $ReturnURL ."?action=paymentpending&tid=". $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } $padata = '&TOKEN='.urlencode($token); $paypal= new MyPayPal(); $httpParsedResponseAr = $paypal->PPHttpPost('GetExpressCheckoutDetails', $padata, $PayPalApiUsername, $PayPalApiPassword, $PayPalApiSignature, $PayPalMode); if("SUCCESS" == strtoupper($httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($httpParsedResponseAr["ACK"])) { header('Location: ' . $ReturnURL . "?action=paymentok &tid=".$httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); Page 30/57 Executing the DoExpressCheckout method with the same parameters. If the DoExpressCheckout method succeed … And the status of payment is Completed we redirect the customer to the corresponding page. And the status of payment is Pending we redirect the customer to the corresponding page. We can also get some more information with the GetExpressCheckoutDetails method. } else { header('Location: ' . $ReturnURL . "?action=paymentfailed&tid=".$httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } } else { header('Location: '.$ReturnURL."?action=paymenterror&tid=" . $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } } ?> PayPal payment notifications and update payment on billing [live.php] CODE COMMENT <?php SOAP login and password. $SOAP_user = 'xxxxxx'; $SOAP_password = 'xxxxxx'; $sandbox = FALSE; include "PortaBillingSoapClient.php"; // Send an empty HTTP 200 OK response to acknowledge receipt of the notification header('HTTP/1.1 200 OK'); Page 31/57 A PHP library for the SOAP connection to porta-billing. When the page is called, we send a 200 OK header to PayPal to inform we got the payment notification. This $item_name $item_number $payment_status $payment_amount $payment_currency $receiver_email $payer_email $transaction_id $custom = = = = = = = = = $_POST['item_name1']; $_POST['item_number1']; $_POST['payment_status']; $_POST['mc_gross']; $_POST['mc_currency']; $_POST['receiver_email']; $_POST['payer_email']; $_POST['txn_id']; $_POST['custom']; $mail_To = "cash@switzernet.com"; $mail_From = "PayPal_IPN@switzernet.com"; $mail_Footer = "\nRegards\n\n--\n\nThis is an automatic message.\n\nhost ".php_uname('n')."\nscript ".__FILE__."\n\n\nSwitzernet ©2014 - Nicolas Bondier\n"; . . . $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() "\r\n"; $mail_Subject = "PayPal new payment notification arrived"; $mail_Body Some variables for all the emails that will be sent to cash [at] switzernet.com. This part set up variables when testing in sandbox mode. if ( $sandbox == TRUE ){ $payment_currency = 'CHF'; $PayPalURL = "www.sandbox.paypal.com"; $mail_To = "developer@domain.com"; . way, PayPal will not resend the message. We get the required data from PayPal to process the payment on Portabilling. = "Notification details.\n\n"; Page 32/57 We also send an email with all the data send by PayPal for verification. foreach ($_POST as $key => $value) { $mailreq .= "$key = $value\n"; $mail_Body .= "$key:$value\n"; } $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else { $PayPalURL = "www.paypal.com"; } $message_id_prefix = $transaction_id . '.' . $item_number; // Build the required acknowledgement message out of the notification just received $req = 'cmd=_notify-validate'; $mailreq = ""; foreach ($_POST as $key => $value) { notification NV pairs $mailreq .= "$key = $value\n"; $value = urlencode(stripslashes($value)); $req .= "&$key=$value"; } // Loop through the // Set up the acknowledgement request headers $header = "POST /cgi-bin/webscr HTTP/1.1\r\n"; $header .= "Host: ".$PayPalURL."\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Connection: close\r\n"; $header .= "Content-Length: " . strlen($req) . "\r\n\r\n"; Page 33/57 A unique message id is generated. We need to save it for keeping a thread in our mailboxes. We build the acknowledgement message with all parameters sent by PayPal. The header for to send to PayPal. // Open a socket for the acknowledgement request $fp = fsockopen('ssl://'.$PayPalURL, 443, $errno, $errstr, 30); // Send the HTTP POST request back to PayPal for validation fputs($fp, $header . $req); while (!feof($fp)) { // While not EOF if (strcmp (chomp($res), "VERIFIED") == 0) { VERIFIED - process notification // Response contains // Authentication protocol is complete - OK to process notification contents $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Subject = "PayPal new payment notification / account:".$item_number." / transaction id:".$transaction_id." / status:".$payment_status.""; $mail_Body $mail_Body = "A new transaction as been processed by paypal.\n"; .= "\n"; Page 34/57 The answer message is posted to PayPal server through secured connection with all the parameters we have received. When posting, we read the content of the message send by PayPal until the end of file of the socket. If PayPal answers VERIFIED, it means the received parameters match with a PayPal payment for which we received a notification. We send an email to @cash with the transaction details. $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body $mail_Body .= .= .= .= .= .= .= .= .= .= .= .= .= .= "**********************************************\n"; "* Transaction details *\n"; "**********************************************\n"; "\n"; "item_name : " . $item_name . "\n"; "item_number : " . $item_number . "\n"; "payment_status : " . $payment_status . "\n"; "payment_amount : " . $payment_amount . "\n"; "payment_currency : " . $payment_currency . "\n"; "receiver_email : " . $receiver_email . "\n"; "payer_email : " . $payer_email . "\n"; "transaction_id : " . $transaction_id . "\n"; "custom trx name : " . $custom . "\n"; $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); file_put_contents("log/".$message_id_prefix.".log","step:transaction_auth ok\n",FILE_APPEND); // Possible processing steps for a payment include the following: // Check that the payment_status is Completed if ( $payment_status == 'Completed' ){ $ServiceAccount = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Account'); $session_id = $ServiceAccount->_login($SOAP_user, $SOAP_password); $ServiceAccount->_setSessionId($session_id); // Getting i_customer $GetAccountInfoRequest = array( 'id' => $item_number ); $GetAccountInfoResponse = $ServiceAccount>get_account_info($GetAccountInfoRequest); $i_customer = ''; Page 35/57 If the payment is Completed, according to the initial submit from PayPal, we connect to portabilling to the SOAP account API to search the customer account to update with the payment. file_put_contents("log/" . $message_id_prefix . ".log","step:payment_completed ok\n",FILE_APPEND); if ( !isset($GetAccountInfoResponse->account_info) ){ // Account does not exist $mail_Subject = "[account_not_found] PayPal new payment notification / account:".$item_number." / transaction id:".$transaction_id." / status:".$payment_status.""; $mail_Body = ""; $mail_Body .= "The tansaction could not be done because the account '".$item_number."' could not be found.\n\n"; $mail_Body .= "Please open or check the account '".$item_number."' and manually add the payment.\n\n"; $mail_Body .= "Once the payment has been manually entered, please answer this email with '[done] PayPal new ...'.\n\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.account_not_found@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'X-Priority: 1' . "\r\n"; $mail_Header .= 'X-MSMail-Priority: High' . "\r\n"; $mail_Header .= 'Importance: High' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; Page 36/57 If the account is not found, we send an email replying the precedent email, informing the account could not be found. $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); file_put_contents("log/".$message_id_prefix.".log","step:account_exist no\n",FILE_APPEND); } else { $i_customer = $GetAccountInfoResponse->account_info->i_customer; $account_currency = $GetAccountInfoResponse->account_info>iso_4217; Else, if the account is found, we get the ID of the customer and the currency. file_put_contents("log/".$message_id_prefix.".log","step:account_exist ok\n",FILE_APPEND); if ( $payment_currency != $account_currency ){ file_put_contents("log/".$message_id_prefix.".log","step:currency_match no\n",FILE_APPEND); $mail_Subject = "[wrong_currency] PayPal new payment notification / account:".$item_number." / transaction id:".$transaction_id." / status:".$payment_status.""; $mail_Body = ""; $mail_Body .= "The tansaction could not be processed because the payment currency '".$payment_currency."' is not the same as the '".$item_number."' account's currency '".$account_currency."'.\n\n"; $mail_Body .= "Please convert and manually process the payment to the customer account of voip account '".$item_number."'.\n\n"; Page 37/57 If the customer currency does not match the currency of the payment, we send an email informing of this and do not process the payment. $mail_Body .= "Once the payment has been manually made, please answer this email with '[done] PayPal new ...'.\n\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.wrong_currency@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else { file_put_contents("log/".$message_id_prefix.".log","step:currency_match ok\n",FILE_APPEND); $ServiceCustomer = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Customer'); $session_id = $ServiceCustomer->_login($SOAP_user, $SOAP_password); $ServiceCustomer->_setSessionId($session_id); $MakeCustomerTransactionRequest = array( 'i_customer' => $i_customer, 'visible_comment' => 'paiement paypal', 'internal_comment' => 'paiement paypal', 'action' => 'Manual payment', Page 38/57 If currencies match, we prepare a payment request on portabilling on the SOAP customer API. 'amount' 'suppress_notification' 'transaction_id' 'h323_conf_id' => => => => $payment_amount, 0, $transaction_id, '' ); try{ // do the transaction $MakeCustomerTransactionResponse = $ServiceCustomer>make_transaction($MakeCustomerTransactionRequest); file_put_contents("log/".$message_id_prefix.".log","step:make_transaction ok\n",FILE_APPEND); $mail_Subject = "[done] PayPal new payment notification / account:".$item_number." / transaction id:".$transaction_id." / status:".$payment_status.""; $mail_Body = ""; $mail_Body .= "A payment of ".$payment_amount." ".$payment_currency." has been processed on account '".$item_number."'\n\n"; $mail_Body .= "YOU HAVE NOTHING TO DO\n\n"; $mail_Body .= "Transaction result :\n"; $mail_Body .= "amount :".$payment_amount."\n"; $mail_Body .= "new balace :".$MakeCustomerTransactionResponse>balance."\n"; $mail_Body .= "id of cdr :".$MakeCustomerTransactionResponse>i_xdr . "\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.done@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; Page 39/57 To process the payment, we use the “try { . . . } catch { . . . }” method to avoid the PHP script to exit on an error. $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } catch (SoapFault $fault) { file_put_contents("log/".$message_id_prefix.".log", "step:make_transaction failed\n",FILE_APPEND); $mail_Subject = "[payment_error] PayPal new payment notification / account:".$item_number." / transaction id:".$transaction_id." / status:".$payment_status.""; $mail_Body = ""; $mail_Body .= "An error occured during the payment on customer account on porta-billing\n\n"; $mail_Body .= "SOAP Fault: (faultcode: {$fault->faultcode}, faultstring: {$fault->faultstring})"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.payment_error@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'X-Priority: 1' . "\r\n"; $mail_Header .= 'X-MSMail-Priority: High' . "\r\n"; Page 40/57 If an error happen, we send an email to cash for informing. $mail_Header .= 'Importance: High' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } Logout from the SOAP customer API. Logout from the SOAP account API. $ServiceCustomer->_logout(); } } $ServiceAccount->_logout(); } } else if (strcmp ($res, "INVALID") == 0) { $mail_From = "IPN@switzernet.com"; $mail_Subject = "INVALID IPN"; $mail_Header = 'From: ' . $mail_From . "\r\n" . 'Reply-To: ' . $mail_To. "\r\n" . 'X-Mailer: PHP/' . phpversion(); $mail_Body = $req; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); If PayPal return INVALID to our script, we send an email with the information to @cash. } } function chomp($string){ return trim(preg_replace('/\s+/', ' ', $string)); } ?> Page 41/57 Function for deleting bad end of line. PostFinance Process Postfinance payments [postfinanceProcess.php] CODE COMMENT <?php Setting language and locales correspondences for PostFinance payment page. if(!isset($_SESSION)){session_start();} $locales = array('FR' => 'fr_FR', 'EN' => 'en_EN', 'DE' => 'de_DE' ); if ( ! isset( $_SESSION['language']['code'] ) ){ $_SESSION['language']['code'] = 'FR'; } if ( isset( $_GET['language'] ) && isset( $locales[ $_GET['language'] ] ) ){ $_SESSION['language']['code'] = $_GET['language']; } Verifying the number format. $number = $_POST['number']; if ( ! preg_match('/^41[0-9]{9}$/', $number) ){ die ("wrong number format $number"); } $credit = ""; if ( isset($_POST['itemprice']) ){ $credit = str_replace(',', '.', $_POST['itemprice']); if ( ! preg_match('/^[0-9]+(\.[0-9]{0,2})?$/', $credit ) ){ Page 42/57 Getting the amount the customer want die ("wrong amount format $credit"); to credit on his account. } } if ( isset( $_POST['paiementType'] ) && $_POST['paiementType'] == "creditcard" ){ $orderTotal = $credit; $params = array('orderID', 'amount', 'currency', 'PSPID', 'Operation', 'logo', 'language', 'paramplus', 'SHASIGN'); usort($params, "cmp"); $hashSeed = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // post account informations $data['PSPID'] = "xxxxxxxxxxxxxxxxxxxx"; // payment informations $data['orderID'] = htmlentities("VOIP-" . convert_to_0XX_XXX_XXXX($number) ); $data['amount'] = (int)($orderTotal*100. ); // Format the number in integer after being mutliplied by 100 to fit to e-pay requirements $data['currency'] = "CHF"; $data['Operation'] = "SAL"; // view informations $data['language'] = $locales[ $_SESSION['language']['code'] ]; $data['logo'] = "https://www.intarnetinc.com/images/switzernet.gif"; $data['paramplus'] = "numeroSIP=".convert_to_0XX_XXX_XXXX($number); $data['TP'] = "http://switzernet.com/public/140819epay/postfinanceTemplate.php"; $request = ""; // Create the signature foreach ($params as $i => $p){ if (isset($data[$p])) Page 43/57 We prepare the parameters to post to PostFinance. To permit to PostFinance to verify the data, we create a string with all defined parameters under the format defined in PostFinance documentation. It is a string of key=value pairs separated with the SHA-IN defined in the PostFinance account. Keys must always be in uppercase. $request .= strtoupper($p)."=".$data[$p] . $hashSeed; } $data['SHASIGN'] = strtoupper(sha1($request)); } ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title></title> <script type="text/javascript"> function postFinancePost(){ var form=document.getElementById("postfinanceForm"); form.submit(); } window.onload = function() { postFinancePost(); }; </script> </head> <body> <FORM id="postfinanceForm" METHOD=post ACTION="https://epayment.postfinance.ch/ncol/prod/orderstandard.asp"> <?php foreach ($params as $i => $p){ if (isset($data[$p])){ echo "<INPUT TYPE=hidden name=$p value=\"$data[$p]\">\n"; } } ?> <br/> <center> <INPUT TYPE=hidden value="ACCEPT"> </center> </FORM> </body> </html> Page 44/57 The string must then be hashed. It is the field to post called SHASIGN. We create a form with our data and tell the browser to submit it to PostFinance. <?php function convert_to_0XX_XXX_XXXX($number){ $number = ereg_replace("[^0-9]", "", $number); if (ereg('^41[0-9]{9}$',$number)){ $number=substr_replace(substr_replace(ereg_replace("^41","0",$number),"",3,0),"-",7,0); return $number; } elseif ( ereg('^0[0-9]{9}$',$number) ) { $number=substr_replace(substr_replace($number,"-",3,0),"-",7,0); return $number; } else { return FALSE; } } Function to convert a phone number to 0XXXXX-XXXX. ?> Postfinance payment notifications and update payment in portabilling [index.php] CODE COMMENT <?php SOAP user and password. $SOAP_user = 'xxxxxxx'; $SOAP_password = 'xxxxxxxxxxxxxxxxxxxxxx'; $postFinanceSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; Page 45/57 Postfinance SHA-OUT defined in PostFinance interface. A PHP library for the SOAP connection to portabilling. include "PortaBillingSoapClient.php"; Test mode is disabled. $test = FALSE; $mail_To $mail_From = "cash@switzernet.com"; = "PostFinance_IPN@switzernet.com"; Some variables for all the emails that will be sent to cash [at] switzernet.com. $mail_Footer = "\nRegards\n\n--\n\nThis is an automatic message.\n\nhost ".php_uname('n')."\nscript ".__FILE__."\n\n\nSwitzernet ©2014 - Nicolas Bondier\n"; if ( $test == TRUE ){ $mail_To = "developer@domain.com"; $postFinanceSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; $mail_From = "TEST_PostFinance_IPN@switzernet.com"; } $message_id_prefix = time() . '-' . md5('postfinance-notification' . $mail_To); if ( isset($_POST['SHASIGN']) && $_POST['SHASIGN'] == ogone_hash_parameters_in( $_POST, $postFinanceSecret ) ){ $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; Page 46/57 This part set up variables in testing mode. Secret may not be the same as we use the test account of PostFinance. A unique message id is generated. We need to save it for keeping a thread in our mailboxes. If the SHASIGN received by PostFinance is the same as the one we calculated in the ogone_hash_parameters_in function, it means the payment notification is valid. We send an email to @cash with the received request. $mail_Subject = "Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "RECEIVED PARAMETER :\n".print_r($_REQUEST,TRUE)."\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); if ( $_POST['STATUS'] == '9' ){ $ServiceAccount = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Account'); $session_id = $ServiceAccount->_login($SOAP_user, $SOAP_password); $ServiceAccount->_setSessionId($session_id); // Getting i_customer $GetAccountInfoRequest = array( 'id' => format_account($_POST['numeroSIP']) ); $GetAccountInfoResponse = $ServiceAccount>get_account_info($GetAccountInfoRequest); if ( !isset($GetAccountInfoResponse->account_info) ){ // Account does not exist $mail_Subject = "[account_not_found] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "The tansaction could not be done because the account '".$_POST['numeroSIP']."' could not be found.\n\n"; $mail_Body .= "Please open or check the account '".$_POST['numeroSIP']."' and manually add the payment.\n\n"; $mail_Body .= "Once the payment has been manually entered, please answer this email with '[done] PostFinance new ...'.\n\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; Page 47/57 In the PostFinance submitted data, the status 9 means the payment is accepted. In the case we connect to portabilling in order to find the account of the customer who paid. If we cannot find the account, an email is send to @cash informing the account could not be found. $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.account_not_found@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'X-Priority: 1' . "\r\n"; $mail_Header .= 'X-MSMail-Priority: High' . "\r\n"; $mail_Header .= 'Importance: High' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else { $i_customer = $GetAccountInfoResponse->account_info->i_customer; $account_currency = $GetAccountInfoResponse->account_info>iso_4217; if ( $_POST['currency'] != $account_currency ){ $mail_Subject = "[account_not_found] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "The tansaction could not be processed because the payment currency '".$_POST['numeroSIP']."' is not the same as the '".$_POST['numeroSIP']."' account's currency '".$account_currency."'.\n\n"; $mail_Body .= "Please convert and manually process the payment to the customer account of voip account '".$_POST['numeroSIP']."'.\n\n"; Page 48/57 Else, we search for the ID of customer and currency of the account before entering the payment. If the currencies do not match, we inform by sending an email to @cash. $mail_Body .= "Once the payment has been manually made, please answer this email with '[done] PostFinance new ...'.\n\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.wrong_currency@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else { $ServiceCustomer = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Customer'); $session_id = $ServiceCustomer->_login($SOAP_user, $SOAP_password); $ServiceCustomer->_setSessionId($session_id); $MakeCustomerTransactionRequest = array( 'i_customer' => $i_customer, 'visible_comment' => 'e-paiement', 'internal_comment' => 'e-paiement postfinance', 'action' => 'Manual payment', 'amount' => $_POST['amount'], 'suppress_notification' => 0, 'transaction_id' => $_POST['PAYID'], 'h323_conf_id' => '' ); Page 49/57 If the currencies match, we connect to the customer service of the SOAP API and prepare the payment request. try{ // do the transaction $MakeCustomerTransactionResponse = $ServiceCustomer>make_transaction($MakeCustomerTransactionRequest); $mail_Subject = "[done] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "A payment of ".$_POST['amount']." ".$_POST['currency']." has been processed on account '".$_POST['numeroSIP']."'\n\n"; $mail_Body .= "YOU HAVE NOTHING TO DO\n\n"; $mail_Body .= "Transaction result :\n"; $mail_Body .= "amount :".$$_POST['amount']."\n"; $mail_Body .= "new balace :".$MakeCustomerTransactionResponse->balance."\n"; $mail_Body .= "id of cdr :".$MakeCustomerTransactionResponse->i_xdr."\n"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.done@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); Page 50/57 If the transaction executes without exception, we reply to the first email with [done] to confirm the payment has been correctly entered. Else the error message is sent to @cash. } catch (SoapFault $fault) { $mail_Subject = "[payment_error] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "An error occured during the payment on customer account on porta-billing\n\n"; $mail_Body .= "SOAP Fault: (faultcode: {$fault->faultcode}, faultstring: {$fault->faultstring})"; $mail_Body .= $mail_Footer; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.payment_error@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'X-Priority: 1' . "\r\n"; $mail_Header .= 'X-MSMail-Priority: High' . "\r\n"; $mail_Header .= 'Importance: High' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } $ServiceCustomer->_logout(); } Page 51/57 } $ServiceAccount->_logout(); } elseif ( $_POST['STATUS'] == '1' ) { $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.unknown_status@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Subject = "[payment cancelled] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = "The payment has been cancelled by customer\n\n"; $mail_Body .= "YOU HAVE NOTHING TO DO\n\n"; $mail_Body .= "RECEIVED PARAMETER :\n".print_r($_REQUEST,TRUE)."\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else { $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '.unknown_status@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; Page 52/57 Payment cancellation notice when the status is 1: the payment has been cancelled by the payer. For other statuses, we send an [unknown status] notification in reply to @cash. $mail_Header .= 'In-Reply-To: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Subject = "[unknown status] Postfinance new payment notification / PAYID:".$_POST['PAYID']." / orderID:".$_POST['orderID']." / STATUS:".$_POST['STATUS'].""; $mail_Body = ""; $mail_Body .= "RECEIVED PARAMETER :\n".print_r($_REQUEST,TRUE)."\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } } else { $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: cash@switzernet.com' . "\r\n"; $mail_Header .= 'Message-Id: <'. $message_id_prefix . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'Content-type: text/plain; charset=utf-8' . "\r\n"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Subject = "Postfinance INVALID payment notification"; $mail_Body = ""; $mail_Body .= "RECEIVED PARAMETER :\n".print_r($_REQUEST,TRUE)."\n Calculated hash : ".ogone_hash_parameters_in( $_POST, $postFinanceSecret ); $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } Page 53/57 If the calculated hash does not correspond to the one sent by PostFinance, we send an email will all the data to @cash. function ogone_hash_parameters_in($parameters = array(), $secretkey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') { $str = ''; $param = array(); /* On s'assure que toutes les clés sont en majuscules et rangées par ordre alphabétique*/ $txt = ''; if ( !empty($parameters) ) { foreach ($parameters as $i => $j) { $param[strtoupper($i)] = $j; } } /* Trier par ordre alphabetique */ ksort($param); if ( !empty($param) ) { foreach($param as $i=> $j) { if ( $j != "" && valid_param(strtoupper($i)) ){ $str .= $i. '=' . $j. $secretkey; $txt .= $i. '=' . $j."\n"; } } } $str = utf8_encode ($str); return strtoupper(sha1($str)); } function format_account( $id ){ $id = preg_replace('/[^0-9,]|,[0-9]*$/','',$id); $id = preg_replace('/^0([0-9]{9})$/', "41$1", $id); return $id; } function valid_param ($key $sha_out_param = array( 'AAVADDRESS' 'AAVCHECK' 'AAVMAIL' The function for calculating the hash from received parameters from PostFinance. The key is a hashed string of value/pair separated by the SHA-OUT secret key defined in e-payment PostFinance account. Each keys must be in uppercase and all value/pair key must be in the alphabetical order. A simple function of formatting account id. The list of valid parameters to include in the hash. The list comes from the following document [pdf]. = ""){ => 1, => 1, => 1, Page 54/57 'AAVNAME' 'AAVPHONE' 'AAVZIP' 'ACCEPTANCE' 'ALIAS' 'AMOUNT' 'BIC' 'BIN' 'BRAND' 'CARDNO' 'CCCTY' 'CN' 'COMPLUS' 'CREATION_STATUS' 'CURRENCY' 'CVCCHECK' 'DCC_COMMPERCENTAGE' 'DCC_CONVAMOUNT' 'DCC_CONVCCY' 'DCC_EXCHRATE' 'DCC_EXCHRATESOURCE' 'DCC_EXCHRATETS' 'DCC_INDICATOR' 'DCC_MARGINPERCENTAGE' 'DCC_VALIDHOURS' 'DIGESTCARDNO' 'ECI' 'ED' 'ENCCARDNO' 'FXAMOUNT' 'FXCURRENCY' 'IBAN' 'IP' 'IPCTY' 'NBREMAILUSAGE' 'NBRIPUSAGE' 'NBRIPUSAGE_ALLTX' 'NBRUSAGE' 'NCERROR' 'NCERRORCARDNO' 'NCERRORCN' => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => => 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, Page 55/57 'NCERRORCVC' 'NCERRORED' 'ORDERID' 'PAYID' 'PM' 'SCO_CATEGORY' 'SCORING' 'STATUS' 'SUBBRAND' 'SUBSCRIPTION_ID' 'TRXDATE' 'VC' => => => => => => => => => => => => 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ); if ( $sha_out_param[$key] ){ return true; } return false; } ?> Page 56/57 Links Ce document : http://switzernet.com/3/public/140923-payments/ Online payment page : http://pay.switzernet.com Switzernet customer interface : https://account.switzernet.com/ * * * Copyright © 2016 by Switzernet Page 57/57