[1'e9e'1 card payments] CREDIT CARD PAYMENTS Document created on 2013-06-24 Nicolas Bondier [pdf][doc][htm] Page 1 of 61 [1'e9e'1 card payments] * * * Copyright © 2016 by Switzernet Contents Introduction .............................................................................................................................................................4 Postfinance integration with portabilling ................................................................................................................4 Postfinance e-payment configuration ..................................................................................................................4 Direclink activation .............................................................................................................................................4 Create an API user...............................................................................................................................................4 Allow access for portabilling slave IP address ....................................................................................................7 Configure return parameters ..............................................................................................................................7 Porta-billing configuration ....................................................................................................................................8 Use PostFinance gateway in Ogone payment processing module on slave. ......................................................8 Page 2 of 61 [1'e9e'1 card payments] Add PostFinance to the Porta-Billing payments systems. ..................................................................................9 Payments on website .............................................................................................................................................10 Payment page .....................................................................................................................................................10 PayPal ..................................................................................................................................................................29 Process PayPal payments..................................................................................................................................29 PayPal payment notifications and update payment on billing .........................................................................34 PostFinance .........................................................................................................................................................45 Process Postfinance payments .........................................................................................................................45 Postfinance payment notifications and update payment in portabilling .........................................................49 Liens .......................................................................................................................................................................61 Page 3 of 61 [1'e9e'1 card payments] Introduction This document describes the configuration and scripts used for setting different payments systems in Porta-Billing 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. Page 4 of 61 [1'e9e'1 card payments] 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 5 of 61 [1'e9e'1 card payments] Page 6 of 61 [1'e9e'1 card payments] 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. 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. Page 7 of 61 [1'e9e'1 card payments] 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/porta-admin/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 ‘epayment.postfinance.ch’ as bellow. sub set_defaults{ my $self = shift; $self->server('e-payment.postfinance.ch'); Page 8 of 61 [1'e9e'1 card payments] $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. Page 9 of 61 [1'e9e'1 card payments] 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 140915-postfinance-notification contain the notification system for PayPal and PostFinance. 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 Page 10 of 61 [1'e9e'1 card payments] │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├── ├── │ │ │ │ │ │ │ ├── ├── ├── ├── └── │ ├── 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 └── ru.js paypalProcess.php postfinanceProcess.php postfinanceTemplate.css postfinanceTemplate.php style Page 11 of 61 [1'e9e'1 card payments] ├── ├── ├── ├── └── 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 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']) ){ Page 12 of 61 [1'e9e'1 card payments] $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); 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, initial-scale=1,maximumscale=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"> Page 13 of 61 HTML begins here. The head content contains information for better displaying on mobile applications and old browser. [1'e9e'1 card payments] <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 } ?> <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) { Page 14 of 61 Getting the localization files (language) for error handling for the customer. Some JavaScript functions for submitting language and currency changes. A function to simplify the selection of a parent element. [1'e9e'1 card payments] 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); } } 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> 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 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"]); Page 15 of 61 [1'e9e'1 card payments] (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> <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) { Page 16 of 61 Starting of the body and the page header. Language selection [1'e9e'1 card payments] if ( $_SESSION['language']['code'] == $ISO ){ echo "<option value='".$ISO."' selected='selected'>".$text."</option>"; } else { echo "<option value='".$ISO."'>".$text."</option>"; } } ?> </select> </fieldset> </form> End of page header </div> </td> </tr> </tbody> </table> </div> 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> Page 17 of 61 When the action received is cancel (from PayPal or PostFinance), we display the corresponding content. [1'e9e'1 card payments] </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> When the action received is paymentok (from PayPal or PostFinance), we display the corresponding content. </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'] == '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 Page 18 of 61 When the action received is paymentpending (from PayPal), we display the corresponding content. [1'e9e'1 card payments] } 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 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> <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"]); Page 19 of 61 Error during the payment (from PayPal), we display the corresponding content. Text when received an action called payment. It refers to the last page before submitting the payment to PostFinance or PayPal. [1'e9e'1 card payments] 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;"> <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'; 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. If the format is correct, we get the account currency to display $connection2=mysql_connect(DB2_SERVER.':'.DB2_SERVER_PORT,DB2_SERVER_USERNAME, it on the web page. DB2_SERVER_PASSWORD) or die(mysql_error().' : erreur de connexion a la base de donnee !'); 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); Page 20 of 61 [1'e9e'1 card payments] 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); 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 http://chir.ag/projects/geoiploc $_SESSION['phone_number'] = $number; 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> Page 21 of 61 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. [1'e9e'1 card payments] <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;text-align:left;" min="5" size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } else { ?> <input id="amountInput" class="input_text" data-parsleyerrors-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 } ?> <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;text-align:left;" min="5" size="10" placeholder="XXX.XX" required value="<?= $_SESSION['postdata']['amount'] ?>" /> <?php } else { ?> <input id="amountInput" class="input_text" data-parsleyerrors-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-errors-container="#amountError" name="currency" onchange="changeCurrency(this)" changeCurrency> <?php foreach ($currencyNotations as $key => $value) { Page 22 of 61 [1'e9e'1 card payments] $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"> <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="pure-button pure-button-primary" value="<?= BUTTON_PAY ?>"/> Page 23 of 61 [1'e9e'1 card payments] </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> <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){ Page 24 of 61 If action parameter is not defined or is not in the list of action triggered before, we display the input fields for selecting the account to credit. Selection of the prefixes. [1'e9e'1 card payments] 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" 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-parsley-length="[4, 4]" name="num_part_2" type="text" maxlength="4" size="5" placeholder="XXXX" value="<?= $part3 ?>" required/> <?php } ?> </td> </tr> Selection of the last 4 digits. 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="pure-button purebutton-primary" value="<?= BUTTON_CONTINUE ?>"/> </td> </tr> Page 25 of 61 Button for validating the form and continuing to last page. [1'e9e'1 card payments] </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"> <?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/060801-web/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/paypalpopup','WIPaypal','toolbar=no, location=no, directories=no, status=no, Page 26 of 61 Footer with logos of the available payment methods. [1'e9e'1 card payments] 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_37x23.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/paypalpopup','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 } ?> </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"]); } Page 27 of 61 [1'e9e'1 card payments] 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(); $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; } ?> Page 28 of 61 Function for detecting mobile devices. This is required for all displaying differences added to the html and CSS. [1'e9e'1 card payments] PayPal Process PayPal payments [paypalProcess.php] CODE COMMENT <?php Starting session and including configuration files and a PayPal class for processing the payment easier. if(!isset($_SESSION)){session_start();} 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. { Page 29 of 61 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. [1'e9e'1 card payments] We get the VOIP number to credit and verify the 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 ) ){ die ("wrong amount format $credit"); } } $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). Page 30 of 61 We get the amount the customer wants to credit on his account. 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]. [1'e9e'1 card payments] '&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). '&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; Page 31 of 61 In the same time, we save the data in the session. [1'e9e'1 card payments] $_SESSION['ShippinDiscount'] = $ShippinDiscount; $_SESSION['ShippinCost'] = $ShippinCost; $_SESSION['GrandTotal'] = $GrandTotal; 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); } else { echo '<div style="color:red"><b>Error : </b>'.urldecode($httpParsedResponseAr["L_LONGMESSAGE0"]).'</div>'; echo '<pre>'; //print_r($httpParsedResponseAr); echo '</pre>'; } } 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']; Page 32 of 61 Else we show the error message. 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. [1'e9e'1 card payments] $ItemTotalPrice = $_SESSION['ItemTotalPrice']; $TotalTaxAmount = $_SESSION['TotalTaxAmount']; $HandalingCost = $_SESSION['HandalingCost']; $InsuranceCost = $_SESSION['InsuranceCost']; $ShippinDiscount = $_SESSION['ShippinDiscount']; $ShippinCost = $_SESSION['ShippinCost']; $GrandTotal $_SESSION['GrandTotal']; We haven’t received the payment yet. = $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). '&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"]) { Page 33 of 61 We also get all the session variables. Preparing the data for DoExpressCheckout method. Executing the DoExpressCheckout method with the same parameters. If the DoExpressCheckout method succeed … And the status of payment is Completed we redirect [1'e9e'1 card payments] header('Location: '. $ReturnURL ."?action=paymentok&tid=". $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); the customer to the corresponding page. } elseif('Pending' == $httpParsedResponseAr["PAYMENTINFO_0_PAYMENTSTATUS"]) { header('Location: '. $ReturnURL ."?action=paymentpending&tid=". $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } And the status of payment is Pending we redirect the customer to the corresponding page. $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"]); } else { header('Location: ' . $ReturnURL . "?action=paymentfailed&tid=".$httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } } else { header('Location: '.$ReturnURL."?action=paymenterror&tid=" . $httpParsedResponseAr["PAYMENTINFO_0_TRANSACTIONID"]); } } We can also get some more information with the GetExpressCheckoutDetails method. ?> PayPal payment notifications and update payment on billing [live.php] CODE COMMENT Page 34 of 61 [1'e9e'1 card payments] <?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'); $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"; Page 35 of 61 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 way, PayPal will not resend the message. We get the required data from PayPal to process the payment on Portabilling. Some variables for all the emails that will be sent [1'e9e'1 card payments] 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"; . . . . $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 We also send an email with all the data send by PayPal for verification. = "Notification details.\n\n"; 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; Page 36 of 61 A unique message id is generated. We need to save it for keeping a thread in our mailboxes. [1'e9e'1 card payments] // Build the required acknowledgement message out of the notification just received $req = 'cmd=_notify-validate'; $mailreq = ""; foreach ($_POST as $key => $value) { pairs $mailreq .= "$key = $value\n"; $value = urlencode(stripslashes($value)); $req .= "&$key=$value"; } // Loop through the notification NV // 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"; // 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) { process notification // Response contains VERIFIED - Page 37 of 61 We build the acknowledgement message with all parameters sent by PayPal. The header for to send to PayPal. 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 [1'e9e'1 card payments] . . . . . // 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 $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 = .= .= .= .= .= .= .= .= .= .= .= .= .= .= .= "A new transaction as been processed by paypal.\n"; "\n"; "**********************************************\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: Page 38 of 61 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. [1'e9e'1 card payments] // 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 = ''; 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 = ""; Page 39 of 61 If the account is not found, we send an email replying the precedent email, informing the account could not be found. [1'e9e'1 card payments] $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"; $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; file_put_contents("log/".$message_id_prefix.".log","step:account_exist ok\n",FILE_APPEND); if ( $payment_currency != $account_currency ){ Page 40 of 61 Else, if the account is found, we get the ID of the customer and the currency. If the customer currency does not [1'e9e'1 card payments] 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"; $mail_Body .= "Once the payment has been manually made, please answer this email with '[done] PayPal new ...'.\n\n"; $mail_Body .= $mail_Footer; match the currency of the payment, we send an email informing of this and do not process the payment. $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); If currencies match, we prepare a } else { Page 41 of 61 [1'e9e'1 card payments] 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); payment request on portabilling on the SOAP customer API. $MakeCustomerTransactionRequest = array( 'i_customer' => $i_customer, 'visible_comment' => 'paiement paypal', 'internal_comment' => 'paiement paypal', 'action' => 'Manual payment', 'amount' => $payment_amount, 'suppress_notification' => 0, 'transaction_id' => $transaction_id, 'h323_conf_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"; Page 42 of 61 To process the payment, we use the “try { . . . } catch { . . . }” method to avoid the PHP script to exit on an error. [1'e9e'1 card payments] $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"; $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; Page 43 of 61 If an error happen, we send an email to cash for informing. [1'e9e'1 card payments] $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"; $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) { Page 44 of 61 [1'e9e'1 card payments] $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)); } 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'; Page 45 of 61 [1'e9e'1 card payments] } 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 ) ){ die ("wrong amount format $credit"); } } 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) ); Page 46 of 61 Getting the amount the customer want to credit on his account. 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 [1'e9e'1 card payments] $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/140819-epay/postfinanceTemplate.php"; $request = ""; // Create the signature foreach ($params as $i => $p){ if (isset($data[$p])) $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> Page 47 of 61 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. 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. [1'e9e'1 card payments] </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> <?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; } } ?> Page 48 of 61 Function to convert a phone number to 0XXXXX-XXXX. [1'e9e'1 card payments] 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'; include "PortaBillingSoapClient.php"; Test mode is disabled. $test = FALSE; $mail_To $mail_From Postfinance SHA-OUT defined in PostFinance interface. A PHP library for the SOAP connection to portabilling. Some variables for all the emails that will be sent to cash [at] switzernet.com. = "cash@switzernet.com"; = "PostFinance_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"; if ( $test == TRUE ){ $mail_To = "developer@domain.com"; $postFinanceSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; $mail_From = "TEST_PostFinance_IPN@switzernet.com"; } Page 49 of 61 This part set up variables in testing mode. Secret may not be the same as we use the test account of PostFinance. [1'e9e'1 card payments] $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"; $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; 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($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']) ); Page 50 of 61 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. [1'e9e'1 card payments] $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 = ""; $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"; Page 51 of 61 If we cannot find the account, an email is send to @cash informing the account could not be found. [1'e9e'1 card payments] 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"; $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, 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. If the currencies match, we connect to the } else { Page 52 of 61 [1'e9e'1 card payments] $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' => '' ); 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 = ""; Page 53 of 61 customer service of the SOAP API and prepare the payment request. If the transaction executes without exception, we reply to the first email with [done] to confirm the payment has been correctly entered. [1'e9e'1 card payments] $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); 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"; Page 54 of 61 [1'e9e'1 card payments] $mail_Header '@ipn.switzernet.com>' $mail_Header '@ipn.switzernet.com>' $mail_Header . "\r\n"; $mail_Header . "\r\n"; $mail_Header . "\r\n"; $mail_Header . "\r\n"; $mail_Header . "\r\n"; .= 'References: <' . $message_id_prefix . . "\r\n"; .= 'In-Reply-To: <'. $message_id_prefix . . "\r\n"; .= 'X-Priority: 1' .= 'X-MSMail-Priority: High' .= 'Importance: High' .= 'Content-type: text/plain; charset=utf-8' .= 'X-Mailer: PHP/' . phpversion() mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } $ServiceCustomer->_logout(); } } $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"; Page 55 of 61 Payment cancellation notice when the status is 1: the payment has been cancelled by the payer. [1'e9e'1 card payments] $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"; $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); } For other statuses, we send an [unknown status] notification in reply to @cash. If the calculated hash does not correspond to the one sent by PostFinance, we } else { $mail_Header = ""; Page 56 of 61 [1'e9e'1 card payments] $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); 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"; } Page 57 of 61 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. [1'e9e'1 card payments] } } $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' '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' The list of valid parameters to include in the hash. The list comes from the following document [pdf]. = ""){ => => => => => => => => => => => => => => => => => => => => => => => A simple function of formatting account id. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, Page 58 of 61 [1'e9e'1 card payments] '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' '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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ); if ( $sha_out_param[$key] ){ return true; } return false; } Page 59 of 61 [1'e9e'1 card payments] ?> Page 60 of 61 [1'e9e'1 card payments] Liens Ce document : http://switzernet.com/3/public/folder/ * * * Copyright © 2016 by Switzernet Page 61 of 61