PAYPAL ADAPTIVE PAYMENTS
AND
BILLING INTEGRATION
Document created on 2014-10-02
Nicolas Bondier
* * *
Copyright © 2014 by Switzernet
Contents
Adding PayPal in porta-billing
Adding to the porta-billing interface
Adding to the customer interface.
Removing PayPal from the “Pay Now” interface
Modify top row if PayPal is choose for redirecting to normal payment page.
Redirect to PayPal preapproval payment page.
This document describes the PayPal integration with our customer web interface and system for adding PayPal adaptive payments.
Legend : Modified code
In the master database, add the new processor:
INSERT INTO Online_Payment_Processors (
processor
,web_link
,handler
,callback
,ext_auth
,obsolete
,remittance
)
VALUES (
'PayPal'
,'https://www.paypal.com/'
,NULL
,NULL
,'N'
,'N'
,'N'
);
Add a PayPal icon in the folder of payments methods icons /home/porta-admin/apache/images/PMIcons/:
Know you would be able to add a new payment system with the PayPal payment method. Login and passwords does not care.
And for a currency, add the new payment system:
The billing cannot process a normal Pay action with PayPal. If no credit card is set and the customer wants to pay, he will see the PayPal in the list of payment methods. It must be removed.
[/home/porta-admin/apache/make_payment.mcomp] line 442
<td><text>Payment Method</text></td>
<td colspan="3"><select name=i_payment_method onChange="javascript:ChangePaymentMethod()">
% foreach my $row (@$payment_methods) {
% if ( $row->{payment_method} ne 'PayPal' ){
<option value="<% $row->{i_payment_method} %>" ext_auth="<% $row->{ext_auth}%>"
<% $row->{i_payment_method} == $ARGS{i_payment_method} ? 'selected':'' %>
payment_method="<% $row->{payment_method} |js %>"><%$row->{name}|h%></option>
% }
% }
[/home/porta-admin/apache/top-table.html] line 16
if ( !$content ) {
if ( $ARGS{href} =~ /^http:\/\/pay\.switzernet.com/ ){
$content = '<li><a target="_blank" onclick="return PB_confirmLeavePage()" href="'.$ARGS{href}.'">'. $str .'<\/a>';
} else {
$content = '<li><a onclick="return PB_confirmLeavePage()" href="'.$ARGS{href}.'">'. $str .'<\/a>';
}
}
[/home/porta-admin/apache/customer_selfcare/top-table.html] line 46
<&|/core.mcomp:.pb100 &>
% my $paypal_result = "";
% if ( defined( $info->{i_credit_card} ) ){
% my $porta_dbh = DBI->connect( "xxxx", "xxxx", "xxxx" ) or die "Connexion impossible à la base de données porta-billing sur porta-billing-master !";
% $paypal_result = $porta_dbh->selectrow_array( 'SELECT pm.name FROM Credit_Cards cc INNER JOIN Payment_Methods pm ON cc.i_payment_method=pm.i_payment_method WHERE i_credit_card = '.$info->{i_credit_card}.' LIMIT 1', undef );
% $porta_dbh->disconnect;
% }
% if ( $processor->{processor} ) {
% if ( $paypal_result ne 'PayPal' ) {
<& /top-table.html:.lset, level => $ph->{level}, i => $cur_numb, is => \$is, href => "/make_payment.html", attr => 'Make Payment', str => '<jstext>mn_Make_Payment</jstext>' &>
% } else {
<& /top-table.html:.lset, level => $ph->{level}, i => $cur_numb, is => \$is, href => "http://pay.switzernet.com?action=payment&i_customer=".$info->{acc}, attr => 'Make Payment', str => '<jstext>mn_Make_Payment</jstext>' &>
% }
% if ($periodical_en && $info->{ppm_enabled} eq 'Y') {
<& /top-table.html:.lset, level => $ph->{level}, i => $cur_numb, is => \$is, href => "/p_payment.html", attr => 'Periodical Payments', str => '<jstext>mn_Periodical_Payments</jstext>' &>
% }
% }
</&>
[/home/porta-admin/apache//switzernet/paypal/paypal_preappoval_info.mcomp]
Code |
Comment |
<%args> $info $card_info </%args>
|
Beginning of the mason script. |
<%perl> my $test_variable = $card_info; my $i_customer = $info->{acc};
my $database = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "; my $host = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "; my $user = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "; my $pass = " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ";
|
Database logins and passwords. |
my $AUTH_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
|
Shared secret key. |
my $TARGET_URL = "http://switzernet.com/3/public/140910-paypal-recurring/paypalPreapprovalProcess.php";
|
URL on which the customer must be redirected. |
my $BUTTON_PARAMS = { 'i_customer' => $i_customer, };
|
Parameters that we will send and hash. |
# GETTING STATUS OF PREAPPORVAL
my $dbh = DBI->connect( "dbi:mysql:$database:$host:xxxx", $user, $pass ) or die "Couldn't connect to database: " . DBI->errstr; my $sql = "SELECT senderEmail , approved FROM CustomerPayPalPreapprovedPayments WHERE i_customer = ? ;"; my $sth = $dbh->prepare($sql);
$sth->execute( $i_customer ) or die DBI->errstr;
my $result = $sth->fetchrow_hashref();
|
Getting the status of the preapproval.
|
my $status_html = ''; my $button_text = 'Give a PayPal authorization'; if ( defined( $result->{approved} ) ){ if ( $result->{approved} ){ $status_html = 'Approved'; $button_text = "Give a new PayPal authorization"; } else { $status_html = 'Authorization pending'; } } else { $status_html = 'No paypal authorization defined'; } $sth->finish(); $dbh->disconnect;
|
Displaying the status. |
# CREATING THE LINK WITH SHA AUTHENTICATION use URI::Escape; use Digest::SHA1 qw(sha1_hex);
# ---
my $SHA1_KEY = ''; my $URL_PARAMS = ''; my $BUTTON_URL = '';
foreach my $key (sort keys %$BUTTON_PARAMS) { $SHA1_KEY .= uri_escape( $key."=".$BUTTON_PARAMS->{$key} ) . $AUTH_KEY; $URL_PARAMS .= uri_escape( $key ) ."=" . uri_escape( $BUTTON_PARAMS->{$key} ) . "&"; } $SHA1_KEY = sha1_hex($SHA1_KEY); $BUTTON_URL = $TARGET_URL . "?" . $URL_PARAMS . "AUTH_KEY=" . $SHA1_KEY;
# ---
|
This is the process to build the URL on which the customer will go for the preapproval.
It was necessary to protect the URL in order to make impossible to customer to override other customers pre-approvals.
The AUTH_KEY parameter will contain a hash of the URL parameters concatenated with the secret key.
This parameter will be verified in the preapproval page that will redirect to PayPal. |
</%perl> <table border="0"> <tr> <td style="width: 23%;"></td> <td><br>Status : <% $status_html %></td> </tr> <tr> <td style="width: 23%;"><br></td> <td><br> <input type="button" onclick="window.location.href='<% $BUTTON_URL %>';" value="<% $button_text %>" /> </td> </tr> </table>
|
Displaying of the status and button. |
[/home/porta-admin/apache/switzernet/paypal/paypal_preappoval_info.mcomp] line 244
<div ID="PayPalDiv" style="display: none; position: absolute;">
<& /switzernet/paypal/paypal_preappoval_info.mcomp, info => $info, card_info => $card_info &>
</div>
[/home/porta-admin/apache/switzernet/paypal/paypal_preappoval_info.mcomp] line 178
if (pm == "PayPal") {
hide_paymentInfo();
// CardNoText.innerHTML = "test";
elemCVV.style.display = 'none'
CardExpTD1.style.display = 'none'
CardExpTD2.style.display = 'none'
NameOnCard.innerHTML = "<text>PM Account Name</text>"
CardNoText.innerHTML = "<text>PM Account number</text>"
elemCityReq.style.display = ''
document.getElementById('PayPalDiv').style.display = "";
document.getElementById('PayPalDiv').style.visibility = "visible";
}
[/home/porta-admin/apache/switzernet/paypal/paypal_preappoval_info.mcomp] line 40
function hide_paymentInfo() {
document.getElementById('CreditCardDiv').style.visibility = 'hidden';
document.getElementById('ExtAuthDiv').style.visibility = 'hidden';
document.getElementById('PayPalDiv').style.visibility = 'hidden';
}
[/home/porta-admin/apache/switzernet/paypal/paypal_preappoval_info.mcomp] line 65
if (ext_auth != 'Y' && meth != '' && meth > 2 && pm != 'PayPal') {
[/home/porta-admin/site_lib/Porta/Tasks.pm] line 754
if($payment_module->is_paypal) {
$self->{i_env} = $old_env;
Porta::TaskLog->info('PayPal must be managed externaly');
$payment_module->clean_is_paypal();
next;
}
my $mail_tmpl = new Porta::Mail_Template;
[/home/porta-admin/site_lib/Porta/Payment.pm] line 67
my $PAYPAL_KEY = 'paypal_not_supported';
[/home/porta-admin/site_lib/Porta/Payment.pm] line 1258
} elsif ( $processor->{processor} eq 'PayPal' ) {
$self->{errstr} = "[custom switzernet message] PayPal is not supported";
$self->{i_env} = $old_env;
$self->{ $PAYPAL_KEY } = 1;
print $LOG localtime() . ": " . $self->{errstr} . "\n";
return undef;
} else {
$self->{errstr} = "Payment Processor $processor->{processor} not supported yet.";
[/home/porta-admin/site_lib/Porta/Payment.pm] line 4612
sub clean_amount_too_small {
my $self = shift;
$self->{ $SMALL_AMOUNT_KEY } = 0;
}
sub is_paypal {
my $self = shift;
return $self->{ $PAYPAL_KEY } ? 1 : 0;
}
sub clean_is_paypal {
my $self = shift;
return $self->{ $PAYPAL_KEY } = 0;
}
[paypalPreapprovalProcess.php]
Code |
Comment |
require_once('include/portabilling.config.php'); require_once("classes/PortaBillingSoapClient.php"); require_once("include/db.config.php"); require_once('PPBootStrap.php');
|
Includes |
$SOAP_user = 'xxxxxxxx'; $SOAP_password = 'xxxxxxxx';
define("DEFAULT_SELECT", "- Select -");
$sipAccount = ''; $i_customer = ''; $account_currency = ''; $startingDate = date("Y-m-d"); $cancelUrl = ""; $returnUrl = "";
|
Some variables used latter. |
$ServiceAccount = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Account'); $session_id = $ServiceAccount->_login($SOAP_user, $SOAP_password); $ServiceAccount->_setSessionId($session_id);
|
Connection to SOAP. |
// GET FROM PORTA-BILLING
if ( isset( $_GET['i_customer'] ) && preg_match('/^[0-9]+$/', $_GET['i_customer'] ) ){
if ( ! isset( $_GET['AUTH_KEY'] ) ){ echo "You are not authenticated !\n"; $ServiceAccount->_logout(); exit; }
|
Get the customer id for which we have to make a preapproval request. The URL is composed of i_customer and an authentication key. The authentication key is a hash of the secret and parameters. |
$AUTH_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; $ALLOWED_GET_PARAMS = array( 'i_customer' => true, );
$GET_PARAMS = $_GET; $URL_PARAMS = ''; $SHA1_KEY = '';
foreach ($ALLOWED_GET_PARAMS as $key => $value) { if ( $value && isset( $GET_PARAMS[$key] ) ){ $SHA1_KEY .= urlencode($key."=".$GET_PARAMS[$key]).$AUTH_KEY; $URL_PARAMS .= urlencode($key)."=".urlencode($value)."&"; } } $SHA1_KEY = preg_replace('/\+/', '%20', $SHA1_KEY); $SHA1_KEY = sha1($SHA1_KEY);
|
Here we have the same secret key defined.
We recalculate the hash from the secret key and paramaters. |
if ( $SHA1_KEY != $_GET['AUTH_KEY'] ){ echo "You are not authenticated !\n"; $ServiceAccount->_logout(); exit; }
|
If the recalculated hash is the same as the one received in parameters, it means the URL comes from the porta-billing server. If not, we leave. |
$i_customer = $_GET['i_customer']; $GetAccountListRequest = array( 'i_customer' => $i_customer, 'offset' => NULL, 'limit' => NULL, 'i_batch' => NULL ); $GetAccountListResponse = $ServiceAccount->get_account_list($GetAccountListRequest); $account_list = $GetAccountListResponse->account_list; foreach ($account_list as $account_info) { if ( $account_info->bill_status != 'C' ){ $sipAccount = $account_info->id; $account_currency = $account_info->iso_4217; break; } }
|
With SOAP, we search for the customer account and currency. |
$returnUrl = "https://account.switzernet.com/customer_info.html?tab=p_i"; $cancelUrl = "https://account.switzernet.com/customer_info.html?tab=p_i"; }
$ServiceAccount->_logout();
|
Here, we define the return URLs to send to PayPal. They will be used for redirection to the porta-billing slave server.
|
if ( $sipAccount == '' ){ echo "No account defined"; exit; }
|
If no account has been found, we leave. |
$requestEnvelope = new RequestEnvelope("en_US"); $preapprovalRequest = new PreapprovalRequest($requestEnvelope, $cancelUrl, $account_currency, $returnUrl, $startingDate); |
Beginning of the construction of the PayPal request. |
$preapprovalRequest->paymentPeriod = 'NO_PERIOD_SPECIFIED'; |
This is the day of each payment. We do not have specified day. Customer can choose to pay anytime. |
$preapprovalRequest->pinType = 'NOT_REQUIRED';
|
No pin is required. |
$preapprovalRequest->feesPayer = 'EACHRECEIVER';
|
Each receiver (currently there is only one). |
$preapprovalRequest->ipnNotificationUrl = 'http://switzernet.com/public/140911-paypal-preapproval-notification/sandbox.php'; |
The URL on which PayPal will send the notifications. |
$preapprovalRequest->endingDate = date('Y-m-d', strtotime('+364 days)) ."\n"; |
The ending date. The default maximum value is one year. We fix this to the maximum for the moment. |
$preapprovalRequest->maxTotalAmountOfAllPayments = '2000';
|
The maximum total amount of all payment is by default $2000. We fix this setting to the maximum too. |
$preapprovalRequest->paymentPeriod = 'NO_PERIOD_SPECIFIED';
|
We do not specify a payment period. Customer can pay at any time. |
$preapprovalRequest->maxAmountPerPayment = '300';
|
It is mandatory to fix a maximum amount per payment. We set 300 which should not block normal customer payments. |
$service = new AdaptivePaymentsService(Configuration::getAcctAndConfig()); try { /* wrap API method calls on the service object with a try catch */ $response = $service->Preapproval($preapprovalRequest); } catch(Exception $ex) { require_once 'Common/Error.php'; exit; }
|
Sending of the approval request. |
$DEBUG = FALSE;
|
Debug variable. |
if ( $DEBUG ){ ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>PayPal Adaptive Payments - Preapproval</title> <link href="Common/sdk.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="Common/sdk_functions.js"></script> </head> <body> <div id="wrapper"> <img src="https://devtools-paypal.com/image/bdg_payments_by_pp_2line.png"/> <div id="response_form"> <h3>Preapproval</h3> <?php $ack = strtoupper($response->responseEnvelope->ack); if($ack != "SUCCESS"){ echo "<b>Error </b>"; echo "<pre>"; print_r($response); echo "</pre>"; } else { echo "<pre>"; print_r($response); echo "</pre>"; // Redirect to paypal.com here $token = $response->preapprovalKey; $payPalURL = 'https://www.sandbox.paypal.com/webscr&cmd=_ap-preapproval&preapprovalkey='.$token; echo "<table>"; echo "<tr><td>Ack :</td><td><div id='Ack'>$ack</div> </td></tr>"; echo "<tr><td>PreapprovalKey :</td><td><div id='PreapprovalKey'>$token</div> </td></tr>"; echo "<tr><td><a href=$payPalURL><b>Redirect URL to Complete Preapproval Authorization</b></a></td></tr>"; echo "</table>"; } require_once 'Common/Response.php'; ?> </div> </div> </body> </html>
|
In case of debug. We display the content of the request and PayPal responses.
A link is generated to continue to the PayPal preapproval page. |
<?php } else { $ack = strtoupper($response->responseEnvelope->ack); if($ack != "SUCCESS"){ echo "<b>Error </b>"; echo "<pre>"; print_r($response); echo "</pre>"; } else { $token = $response->preapprovalKey; $mysqli = new mysqli(DB_SERVER, DB_SERVER_USERNAME, DB_SERVER_PASSWORD, DB_DATABASE); if (mysqli_connect_errno()) { printf("Échec de la connexion : %s\n", mysqli_connect_error()); exit(); } $stmt = $mysqli->prepare("REPLACE INTO `CustomerPayPalPreapprovedPayments` (i_customer, preapproval_key) VALUES (?, ?)"); $i_customer = chomp($i_customer); $token = chomp($token); $stmt->bind_param('is', $i_customer, $token ); $stmt->execute(); if ($stmt->error){ alert($stmt->error); } $stmt->close(); $mysqli->close();
$payPalURL = 'https://www.sandbox.paypal.com/webscr&cmd=_ap-preapproval&preapprovalkey='.$token; header('Location: '.$payPalURL); } }
|
If we are not in debugging mode, we verify the answer from PayPal.
If the preapproval request is accepted, we save the preapproval key we received in the database and the i_customer.
|
/* DROP TABLE `CustomerPayPalPreapprovedPayments`; CREATE TABLE `CustomerPayPalPreapprovedPayments` ( id INT(10) NOT NULL AUTO_INCREMENT, i_customer INT(10) UNSIGNED NOT NULL UNIQUE, preapproval_key VARCHAR(32) NOT NULL UNIQUE, startingDate DATETIME, endingDate DATETIME, currency_code VARCHAR(3), approved TINYINT(1) NOT NULL DEFAULT 0, senderEmail VARCHAR(255), PRIMARY KEY (id), KEY (i_customer), KEY (preapproval_key), KEY (startingDate), KEY (endingDate), KEY (approved) ); */
|
|
function alert( $string = '' ){ echo "<script> $(document).ready(function (){ alert(\"$string\"); }); </script>"; } ?>
|
An alert function for debugging |
Code |
Comment |
<?php require_once("configure.php"); $sandbox = TRUE;
|
Files required |
header('HTTP/1.1 200 OK');
|
Send an empty HTTP 200 OK response to acknowledge receipt of the notification |
$starting_date = $_POST['starting_date']; $currency_code = $_POST['currency_code']; $sender_email = $_POST['sender_email']; $preapproval_key = $_POST['preapproval_key']; $approved = $_POST['approved']; $transaction_type = $_POST['transaction_type']; $preapproval_key = chomp($preapproval_key);
|
Assign payment notification values to local variables |
$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 preferences. |
if ( $sandbox == TRUE ){ $payment_currency = 'CHF'; $PayPalURL = "www.sandbox.paypal.com"; $mail_To = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
$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 = "Notification details on ".date("Y-m-d H-i-s").":\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"; }
|
If we are in sandbox mode, we send an email with received data, change the recipeits of the emails and change the connections settings.
|
$message_id_prefix = 'paypal-approval.'.$preapproval_key;
$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";
|
Fixing an id for the future email that will be sent and replay in threads.
|
$req = 'cmd=_notify-validate'; $mailreq = "";
foreach ($_POST as $key => $value) { $mailreq .= "$key = $value\n"; $value = urlencode(stripslashes($value)); $req .= "&$key=$value"; }
|
Build the required acknowledgement message out of the notification just received
Add 'cmd=_notify-validate' to beginning of the acknowledgement.
Loop through the notification NV pairs. Encode the values and add the NV pairs to the acknowledgement. |
$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";
|
Set up the acknowledgement request headers.
|
$fp = fsockopen('ssl://'.$PayPalURL, 443, $errno, $errstr, 30);
|
Open a socket for the acknowledgement request |
fputs($fp, $header . $req);
|
Send the HTTP POST request back to PayPal for validation |
$reception = "["; $i = 0; while (!feof($fp)) { // While not EOF $res = fgets($fp, 1024); // Get the acknowledgement response
|
Get the acknowledgement response in the socket. |
$reception .= "[".chomp($res)."]\n"; if (strcmp (chomp($res), "VERIFIED") == 0) { // Response contains VERIFIED - process notification // 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 preapproval notification / preapproval_key:".$preapproval_key." / sender_email:".$sender_email." / approved:".$approved.""; $mail_Body = "A preapproval as been sent by paypal.\n"; $mail_Body .= "\n"; $mail_Body .= "**********************************************\n"; $mail_Body .= "* Preapproval details *\n"; $mail_Body .= "**********************************************\n"; $mail_Body .= "\n"; $mail_Body .= "starting_date : " . $starting_date . "\n"; $mail_Body .= "currency_code : " . $currency_code . "\n"; $mail_Body .= "sender_email : " . $sender_email . "\n"; $mail_Body .= "preapproval_key : " . $preapproval_key . "\n"; $mail_Body .= "approved : " . $approved . "\n"; $mail_Body .= "transaction_type : " . $transaction_type . "\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header);
|
The answer is verified.
We send the preapproval depails by email. |
if ( $approved == 'true' ){ // The customer approved $i_customer = '';
$con = mysql_connect(DB_SERVER,DB_SERVER_USERNAME,DB_SERVER_PASSWORD); mysql_select_db(DB_DATABASE) or die(mysql_error()); $req = "SELECT i_customer FROM `CustomerPayPalPreapprovedPayments` WHERE preapproval_key = '".$preapproval_key."' LIMIT 1"; $result = mysql_query($req); $value = mysql_fetch_object($result); $i_customer = $value->i_customer;
|
The customer approved, we search the i_customer in the local database from the preapproval key. |
if ( $i_customer == '' ){ $mail_Subject = "[preapproval_key not found] PayPal preapproval notification / preapproval_key:".$preapproval_key." / sender_email:".$sender_email." / approved:".$approved.""; $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"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Body = "Could not find the preapproval key [".$preapproval_key."] in database. The approvement is done but the account can not been charged\n"; $mail_Body .= var_dump($stmt); $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header);
|
We could not find the preapproval key.
We send an email for informing of the error. |
} else { $req = "UPDATE `CustomerPayPalPreapprovedPayments` SET approved = 1, currency_code = '".$currency_code."', senderEmail = '".$sender_email."', startingDate='".$starting_date."' WHERE preapproval_key ='".$preapproval_key."' LIMIT 1"; $result = mysql_query($req);
if (!$result){ $mail_Subject = "[unable to process notification] PayPal preapproval notification / preapproval_key:".$preapproval_key." / sender_email:".$sender_email." / approved:".$approved.""; $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"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Body = "Could not update the preapproval key in database. The approvement is done but the account will not been charged\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); } else {
|
We found the key and the customer and we updated the database,
We send an email to cash as confirmation. |
$mail_Subject = "[done] PayPal preapproval notification / preapproval_key:".$preapproval_key." / sender_email:".$sender_email." / approved:".$approved.""; $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"; $mail_Header .= 'X-Mailer: PHP/' . phpversion() . "\r\n"; $mail_Body = "The preapproval key is saved in database. The payments will be processed automatically!\n"; $mail_Body .= $mail_Footer; mail($mail_To, $mail_Subject, $mail_Body, $mail_Header); }
|
We were not able to update the database.
An email is sent.
|
} $mysql->close();
|
Closing the locale database. |
} else if (strcmp ($res, "INVALID") == 0) { // Authentication protocol is complete - begin error handling // Send an email announcing the IPN message is INVALID $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 the answer was invalid, we inform the notification is no valid. |
} } fclose($fp); // Close the file
|
Closing the socket with PayPal. |
$reception .= "]"; //mail($mail_To, "Fin de la transaction IPN (index)", "Fin de la transaction IPN (index)\n\n".$reception, $mail_From);
|
|
function chomp($string){ return trim(preg_replace('/\s+/', ' ', $string)); }
|
Removing some bad characters from the string. |
?>
|
|
Code |
Comment |
<?php chdir(dirname(__FILE__))."\n";
|
This script run in crontab. We move to the current directory for using relative paths to required files. |
require_once("../include/portabilling.config.php"); require_once("../include/paypal.config.php"); require_once("../include/db.config.php"); require_once("../classes/PortaBillingSoapClient.php"); require_once('../PPBootStrap.php');
|
Required configuration and classes. |
// Lire la BDD des paiements preappouvés
$mysqli_local = new mysqli(DB_SERVER, DB_SERVER_USERNAME, DB_SERVER_PASSWORD, DB_DATABASE);
$query = "SELECT i_customer, preapproval_key, startingDate, endingDate, currency_code, approved, senderEmail FROM `CustomerPayPalPreapprovedPayments` WHERE approved = 1 AND ( startingDate < NOW() OR startingDate IS NULL ) AND ( endingDate > NOW() OR endingDate IS NULL OR endingDate = '0000-00-00 00:00:00')";
/* Vérifie la connexion */ if (mysqli_connect_errno()) { printf("Échec de la connexion : %s\n", mysqli_connect_error()); exit(); }
$preappouved = array();
if ($stmt = $mysqli_local->prepare($query)) { $stmt->execute(); $stmt->bind_result($i_customer, $preapproval_key, $startingDate, $endingDate, $currency_code, $approved, $senderEmail); while ($stmt->fetch()) { $preappouved[$i_customer] = array( 'preapproval_key' => $preapproval_key, 'startingDate' => $startingDate, 'endingDate' => $endingDate, 'currency_code' => $currency_code, 'approved' => $approved, 'senderEmail' => $senderEmail); #printf ("%s %s %s %s %s %s %s \n", $i_customer, $preapproval_key, $startingDate, $endingDate, $currency_code, $approved, $senderEmail); } $stmt->close(); }
$mysqli_local->close();
|
Connection to the local database. We get all the preapproved payments.
|
$session_id = 0;
$ServiceCustomer = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Customer'); $session_id = $ServiceCustomer->_login(PB_SOAP_LOGIN, PB_SOAP_PASSWORD); $ServiceCustomer->_setSessionId($session_id);
$ServiceAccount = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Account'); $ServiceAccount->_login(PB_SOAP_LOGIN, PB_SOAP_PASSWORD); $ServiceAccount->_setSessionId($session_id);
$ServiceInvoice = new PortaBillingSoapClient('https://slave.switzernet.com:8444', 'Admin', 'Invoice'); $ServiceInvoice->_login(PB_SOAP_LOGIN, PB_SOAP_PASSWORD); $ServiceInvoice->_setSessionId($session_id);
|
Connection to SOAP for the following services: Customer, Account, Invoice.
|
$mysqli = new mysqli(PB_SLAVE_DB_SERVER, PB_SLAVE_DB_SERVER_USERNAME, PB_SLAVE_DB_SERVER_PASSWORD, PB_SLAVE_DB_DATABASE, PB_SLAVE_DB_SERVER_PORT);
$mysqli_master = new mysqli(PB_MASTER_DB_SERVER, PB_MASTER_DB_SERVER_USERNAME, PB_MASTER_DB_SERVER_PASSWORD, PB_MASTER_DB_DATABASE, PB_MASTER_DB_SERVER_PORT);
|
Connection to the slave and master MySQL databases. |
$minAllowedPayments = array();
$query = "SELECT pm.name, mac.min_allowed_payment, mac.iso_4217 as currency FROM Payment_Methods pm INNER JOIN Merchant_Acct_Payment_Methods mapm ON pm.i_payment_method = mapm.i_payment_method INNER JOIN Merchant_Account_Currency mac ON mapm.i_merchant_account = mac.i_merchant_account WHERE pm.name = 'PayPal';";
if ( $stmt = $mysqli->prepare($query) ) { $stmt->execute(); $stmt->bind_result($name, $min_allowed_payment, $currency); while ( $stmt->fetch() ) { $minAllowedPayments[$currency] = $min_allowed_payment; } $stmt->close(); } else { $minAllowedPayments['USD'] = 10; $minAllowedPayments['EUR'] = 10; $minAllowedPayments['CHF'] = 10; }
|
Getting the minimum allowed payments for the payment system in the porta-billing. |
foreach ($preappouved as $i_customer => $preappouved_info) {
|
Loop for each preapproval found in the database. |
$GetCustomerInfoRequest = array( 'i_customer' => $i_customer ); $customer_info = new stdClass(); // Getting customer info try { $GetCustomerInfoResponse = $ServiceCustomer->get_customer_info($GetCustomerInfoRequest); $customer_info = $GetCustomerInfoResponse->customer_info; //print_r($customer_info); echo "\n#\n# CUSTOMER : " , $i , " = " , $customer_info->name , "\n#\n\n"; } catch(Exception $ex) { echo "Couldn't get customer info for i_customer ", $i_customer, "\n"; continue; } //print_r($customer_info);
|
We get the customer info we will need later. |
if ( $customer_info->ppm_enabled != 'Y' ) { echo "Periodical payment not enabled for i_customer ", $i_customer, "\n"; continue; }
|
If the customer do not have periodical payment enabled in the billing, we do not process. |
// Getting payment method info (check if it is set to PayPal in its account). $GetCustomerPaymentMethodInfoRequest = array( 'i_customer' => $i_customer ); $payment_method = new stdClass(); try { $GetCustomerPaymentMethodInfoResponse = $ServiceCustomer->get_payment_method_info($GetCustomerPaymentMethodInfoRequest); $payment_method_info = $GetCustomerPaymentMethodInfoResponse->payment_method_info; } catch(Exception $ex){ echo "Couldn't get payment_method for i_customer ", $i_customer, "\n"; continue; }
|
We check if the customer saved a payment info. If it is not available, we do not process. |
if ( $payment_method_info->payment_method != 'PayPal' ) { echo "PayPal is not the payment method defined for i_customer ", $i_customer, "\n"; continue; }
|
If the payment info is different from PayPal, we do not process the customer. |
$query = "SELECT pp.i_periodical_payment, ppp.description, pp.amount, pp.balance_threshold, pp.last_payment, pp.from_date, pp.to_date, CURDATE() FROM Periodical_Payments pp INNER JOIN Periodical_Payments_Period ppp ON pp.i_periodical_payment_period = ppp.i_periodical_payment_period WHERE i_object = ? AND object = 'customer' AND discontinued = 'N' AND frozen = 'N' AND from_date <= CURDATE() AND to_date >= CURDATE() ORDER BY stamp DESC LIMIT 1";
if ( $stmt = $mysqli->prepare($query) ) { $stmt->bind_param("i", $i_customer); $stmt->execute(); $stmt->bind_result($i_periodical_payment, $description, $amount, $balance_threshold, $last_payment, $from_date, $to_date, $CURDATE); if ( $stmt->fetch() ){ printf(" i_periodical_payment:%d\n description:%s\n amount:%s\n balance_threshold:%s\n last_payment:%s\n from_date:%s\n to_date:%s\n CURDATE:%s\n", $i_periodical_payment, $description, $amount, $balance_threshold, $last_payment, $from_date, $to_date, $CURDATE); echo " balance:".$customer_info->balance."\n"; } else { echo "No record in Periodical_Payments for i_customer ", $i_customer, "\n"; continue; } $stmt->close(); }
|
The query for getting the periodical payment settings.
We get the current available settings for the customer.
If no record is available, we pass to the next customer. |
$timestamp = strtotime( $CURDATE );
|
Getting the timestamp from MySQL database. |
if ( $description == 'monthly' ){ $from_date_timestamp = strtotime( $from_date ); $to_date_timestamp = strtotime( $to_date ); $today_day_of_month = date('j', $timestamp ); if ( date('n',$timestamp) == date('n',$to_date_timestamp) ){ $day_of_month_to_bill = min( date( "t", $timestamp ) , date( "j", $to_date_timestamp ) ); } else { $day_of_month_to_bill = min( date( "t", $timestamp ) , date( "j", $from_date_timestamp ) ); } echo "\n[".date('Y-m-d')."] "; if ( $today_day_of_month == $day_of_month_to_bill && $last_payment != $CURDATE ){ echo "Customer must be charged on ".date('Y-m-d',$timestamp)."\n"; } else { echo "Today ".date('Y-m-d',$timestamp)." is not the billing date\n"; continue; }
|
Starting to read the customer periodical settings.
Here we process the monthly recurring payments.
We define if the current day is the day to execute the payment.
The day to bill is the same day of the month than the starting date (We make the payment earlier in case the day is not present for the current month. Ex 28 days in february). |
if ( $amount == 0 ){ if ( $customer_info->balance < $minAllowedPayments[$customer_info->iso_4217] ){ echo "Payment (".$customer_info->balance.") must be greater or equal to ".$minAllowedPayments[$customer_info->iso_4217]."\n"; continue; } update_last_payment_record( $i_periodical_payment ); $transaction_result = make_transaction( $customer_info->i_customer, $customer_info->balance, $customer_info->iso_4217 ); if ( $transaction_result ){ execute_pb_payment( $customer_info->i_customer, $customer_info->balance, $transaction_result ); } } else {
if ( $amount < $minAllowedPayments[$customer_info->iso_4217] ){ echo "Payment (".$customer_info->balance.") must be greater or equal to ".$minAllowedPayments[$customer_info->iso_4217]."\n"; continue; } update_last_payment_record( $i_periodical_payment ); $transaction_result = make_transaction( $customer_info->i_customer, $amount, $customer_info->iso_4217 ); if ( $transaction_result ){ execute_pb_payment( $customer_info->i_customer, $amount, $transaction_result ); } }
|
We process the amount rules here. $amount equal to 0 is for “Pay Balance” option.
If $amount is greater than 0, we must pay the $amount variable.
We verify this amount is greater than the minimum allowed payment.
We execute the PayPal payment.
We update the master database with the current day for not processing twice or more the payment on the same day.
|
} elseif ( $description == 'weekly' ){ $from_date_timestamp = strtotime( $from_date ); $to_date_timestamp = strtotime( $to_date ); $today_day_of_week = date('N', $timestamp ); if ( date('Y', $timestamp) == date('Y', $to_date_timestamp) && date('W', $timestamp) == date('W', $to_date_timestamp) ){ $day_of_week_to_bill = date( "N", $to_date_timestamp ); } else { $day_of_week_to_bill = date( "N", $from_date_timestamp ); } if ( $today_day_of_month == $day_of_month_to_bill && $last_payment != $CURDATE ){ echo "Customer must be charged on ".date('Y-m-d',$timestamp)."\n"; } else { echo "This is not the billing date ".date('Y-m-d',$timestamp)."\n"; continue; }
|
Here we process the weekly recurring payments.
We define if the current day is the day to execute the payment.
The day to bill is the same day of the week than the starting date. |
if ( $amount == 0 ){ if ( $customer_info->balance < $minAllowedPayments[$customer_info->iso_4217] ){ echo "Payment (".$customer_info->balance.") must be greater or equal to ".$minAllowedPayments[$customer_info->iso_4217]."\n"; continue; } update_last_payment_record( $i_periodical_payment ); $transaction_result = make_transaction( $customer_info->i_customer, $customer_info->balance, $customer_info->iso_4217 ); if ( $transaction_result ){ execute_pb_payment( $customer_info->i_customer, $customer_info->balance, $transaction_result ); } } else { if ( $amount < $minAllowedPayments[$customer_info->iso_4217] ){ echo "Payment (".$customer_info->balance.") must be greater or equal to ".$minAllowedPayments[$customer_info->iso_4217]."\n"; continue; } $transaction_result = make_transaction( $customer_info->i_customer, $amount, $customer_info->iso_4217 ); update_last_payment_record( $i_periodical_payment ); if ( $transaction_result ){ execute_pb_payment( $customer_info->i_customer, $amount, $transaction_result ); } }
|
We process the amount rules here. $amount equal to 0 is for “Pay Balance” option.
If $amount is greater than 0, we must pay the $amount variable.
We verify this amount is greater than the minimum allowed payment.
We execute the PayPal payment.
We update the master database with the current day for not processing twice or more the payment on the same day.
|
} elseif ( $description == 'Balance Driven' ){
if ( $last_payment == $CURDATE ) { echo "A payment has been charged today\n"; continue; } if ( $customer_info->balance < $balance_threshold ){ echo "Balance (".$customer_info->balance.") did not reach the balance threshold (".$balance_threshold.")\n"; continue; } if ( $amount < $minAllowedPayments[$customer_info->iso_4217] ){ echo "Minimum (".$customer_info->balance.") payment must be greater or equal to ".$minAllowedPayments[$customer_info->iso_4217]."\n"; continue; } $transaction_result = make_transaction( $customer_info->i_customer, $amount, $customer_info->iso_4217 ); update_last_payment_record( $i_periodical_payment ); if ( $transaction_result ){ execute_pb_payment( $customer_info->i_customer, $amount, $transaction_result ); } } }
|
In case of “Balance Driven”, we do not need to verify the day. Except for verifying we did not charge the account on the current day.
If the balance is lower than the balance threshold, we do not need to pay.
We verify that the minimum allowed amount is reached.
|
$mysqli->close(); $mysqli_master->close(); $ServiceInvoice->_logout(); $ServiceAccount->_logout(); $ServiceCustomer->_logout();
|
Closing all services and MySQL connections. |
function make_transaction( $i_customer, $amount, $currency ){ global $preappouved;
|
This is the function for processing the payment with payPal.
|
//print_r($preappouved);
echo "we will make a transaction for i_customer ".$i_customer." of ".$amount." ".$currency."\n";
$actionType = "PAY"; //$returnUrl = 'http://' . $_SERVER['SERVER_NAME'] . ':' . dirname($_SERVER['PHP_SELF'])."/"; //$cancelUrl = 'http://' . $_SERVER['SERVER_NAME'] . ':' . dirname($_SERVER['PHP_SELF'])."/"; $returnUrl = "https://account.switzernet.com/customer_info.html"; $cancelUrl = "https://account.switzernet.com/customer_info.html";
$receiver = array(); $receiver[0] = new Receiver(); $receiver[0]->email = RECEIVER_EMAIL; $receiver[0]->amount = $amount; $receiver[0]->primary = "false"; $receiver[0]->paymentType = "SERVICE";
// print_r($receiver); $receiverList = new ReceiverList($receiver);
|
Definition of the main parameters. Return URL and cancel URL are mandatory even if they will not be used.
Receivers are the recipient of the payment.
|
// print_r($receiverList); $payRequest = new PayRequest(new RequestEnvelope("en_US"), $actionType, $cancelUrl, $currency, $receiverList, $returnUrl); $payRequest->preapprovalKey = $preappouved[$i_customer]['preapproval_key']; $payRequest->ipnNotificationUrl = IPN_NOTIFICATION_URL; $payRequest->senderEmail = $preappouved[$i_customer]['senderEmail'];
$service = new AdaptivePaymentsService(Configuration::getAcctAndConfig()); try { /* wrap API method calls on the service object with a try catch */ $response = $service->Pay($payRequest); } catch(Exception $ex) { require_once '../Common/Error.php'; }
|
Sending the pay request. |
$ack = strtoupper($response->responseEnvelope->ack);
if($ack != "SUCCESS") { echo "ERROR\n"; send_info_mail("[error] PayPal periodic payment", "Error during automatic payment with PayPal for i_customer $i_customer \n\n-------------\n\n". print_r($response, TRUE) , $payKey ); return FALSE; } else { // Update the database with the day of payment. $payKey = $response->payKey; if(($response->paymentExecStatus == "COMPLETED" )) { send_info_mail("PayPal periodic payment / status: paid ", "i_customer: $i_customer \n\nDATA:\n " . print_r($response, TRUE) , $payKey ); return $payKey; } else { send_info_mail("[unknown] PayPal periodic payment / status: unknown", "Unknown status of payment with PayPal for i_customer $i_customer \n\n-------------\n\n". print_r($response, TRUE) , $payKey ); } } return FALSE; }
|
Getting the answer from PayPal.
We send email for success or errors.
The function return false if the transaction failed or the pay key if succeed.
|
function execute_pb_payment( $i_customer, $amount, $transaction_id ){ global $ServiceCustomer;
$MakeCustomerTransactionRequest = array( 'i_customer' => $i_customer, 'visible_comment' => 'paiement paypal', 'internal_comment' => 'paiement paypal', 'action' => 'Manual payment', 'amount' => $amount, 'suppress_notification' => 0, 'transaction_id' => $transaction_id, 'h323_conf_id' => '' ); try { $MakeCustomerTransactionResponse = $ServiceCustomer->make_transaction($MakeCustomerTransactionRequest); } catch (SoapFault $fault) { echo "Failed to update porta-billing\n"; send_info_mail("[error] PayPal periodic payment", "Error during payment on porta-billing \n ".$fault, $transaction_id, true ); return false; } send_info_mail("[done] PayPal periodic payment : conpleted", "Payment was entered in porta-billing. \nRequest : \n\n ------------ \n\n".print_r($MakeCustomerTransactionRequest, TRUE), $transaction_id, true ); return true; }
|
Function for executing the payments on porta-billing with SOAP.
We send email on success or errors.
|
function update_last_payment_record ( $i_periodical_payment ) { global $mysqli_master; echo "update_last_payment_record\n"; if ( isset( $i_periodical_payment ) && preg_match( '/^[0-9]+$/', $i_periodical_payment ) ){ $query = "UPDATE Periodical_Payments SET last_payment = CURDATE() WHERE i_periodical_payment = ?"; $stmt = $mysqli_master->prepare($query); if ( false===$stmt ) { echo $mysqli_master->error; } $stmt->bind_param('i', $i_periodical_payment ); $stmt->execute(); $stmt->close(); } }
|
The function for updating the last payment record in the Periodical_Payments in the master database. |
function send_info_mail( $subject, $body, $transaction_id, $answer = false ){
$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_To = "nicolas.bondier@switzernet.com.test-google-a.com"; $mail_Header = ""; $mail_Header .= 'From: ' . $mail_From . "\r\n"; $mail_Header .= 'Reply-To: '.$mail_To . "\r\n"; if ( $answer == false ){ $mail_Header .= 'Message-Id: <'. $transaction_id . '@ipn.switzernet.com>' . "\r\n"; } else { $mail_Header .= 'Message-Id: <'. $transaction_id . '.' . time() . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'References: <' . $transaction_id . '@ipn.switzernet.com>' . "\r\n"; $mail_Header .= 'In-Reply-To: <'. $transaction_id . '@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, $subject, $body, $mail_Header);
}
?>
|
The function for sending information emails.
Transaction id is used for the messages ids and keeping emails in threads.
|
Ce document : http://switzernet.com/3/public/141030-paypal-adaptive/
Switzernet customer interface : https://account.switzernet.com/
* * *
Copyright © 2014 by Switzernet