Are you working for e-commerce project? Then you should have a better and secure payment system. I suggest you setup you project with BrainTree API, it is very powerful and quick. This is accepting most commonly used Cards, PayPal, Android Pay and etc. This post will explain you how to design a cart and order system with BrainTree payment process using PHP and MySQL. Take a quick look the live demo and try demo transaction.
Download Script Live Demo
Database Design
To build the eCommerce system, you have to create four basic tables such as Users, Producs, Orders and Cart.
User Table
User table contains all the users registration details.
CREATE TABLE Users(
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(100),
email VARCHAR(100)
);
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(100),
email VARCHAR(100)
);
Products Table
This contains all of the product details.
CREATE TABLE Products(
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(200),
product_desc TEXT,
price FLOAT
);
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(200),
product_desc TEXT,
price FLOAT
);
Orders Table
User successful orders store here with BrainTree status code.
CREATE TABLE Orders(
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id_fk INT,
created INT,
price FLOAT,
FOREIGN KEY(user_id_fk) REFERENCES Users(user_id)
);
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id_fk INT,
created INT,
price FLOAT,
FOREIGN KEY(user_id_fk) REFERENCES Users(user_id)
);
Cart Table
This table contains all the user cart details. Cart order_id_fk is a foreign key, it references to Orders order_id. This will update once the user Order is successful.
CREATE TABLE Cart(
cart_id INT PRIMARY KEY AUTO_INCREMENT,
product_id_fk INT,
user_id_fk INT,
order_id_fk INT,
cart_status enum('0','1') DEFAULT '0',
FOREIGN KEY(product_id_fk) REFERENCES Products(product_id),
FOREIGN KEY(user_id_fk) REFERENCES Users(user_id),
FOREIGN KEY(order_id_fk) REFERENCES Orders(order_id)
);
cart_id INT PRIMARY KEY AUTO_INCREMENT,
product_id_fk INT,
user_id_fk INT,
order_id_fk INT,
cart_status enum('0','1') DEFAULT '0',
FOREIGN KEY(product_id_fk) REFERENCES Products(product_id),
FOREIGN KEY(user_id_fk) REFERENCES Users(user_id),
FOREIGN KEY(order_id_fk) REFERENCES Orders(order_id)
);
Payment Form & System Design
This project contains three folders called js,includes and uploads with PHP files.
braintree
-- braintree.php //BrainTree library file
js
-- jquery.min.js
-- jquery.creditCardValidator.js
index.php
cardProcess.php
functions.php
db.php
-- braintree.php //BrainTree library file
js
-- jquery.min.js
-- jquery.creditCardValidator.js
index.php
cardProcess.php
functions.php
db.php
Payment form contains Card Number, Card Name, Expiry Date and CVV(Secure Number).
HTML5 Code
Simple HTML5 code
<form method="post" id="paymentForm">
Payment details
<ul>
<li>
<label>Card Number </label>
<input type="text" name="card_number" id="card_number" maxlength="20" placeholder="1234 5678 9012 3456"/>
</li>
<li>
<label>Name on Card</label>
<input type="text" name="card_name" id="card_name" placeholder="Srinivas Tamada"/>
</li>
<li class="vertical">
<ul>
<li>
<label>Expires</label>
<input type="text" name="expiry_month" id="expiry_month" maxlength="2" placeholder="MM" />
<input type="text" name="expiry_year" id="expiry_year" maxlength="2" placeholder="YY" />
</li>
<li>
<label>CVV</label>
<input type="text" name="cvv" id="cvv" maxlength="3" placeholder="123" />
</li>
</ul>
</li>
<li>
<input type="submit" id="paymentButton" value="Proceed" disabled="true" class="disable">
</li>
</ul>
</form>
<div id="orderInfo"></div>
Payment details
<ul>
<li>
<label>Card Number </label>
<input type="text" name="card_number" id="card_number" maxlength="20" placeholder="1234 5678 9012 3456"/>
</li>
<li>
<label>Name on Card</label>
<input type="text" name="card_name" id="card_name" placeholder="Srinivas Tamada"/>
</li>
<li class="vertical">
<ul>
<li>
<label>Expires</label>
<input type="text" name="expiry_month" id="expiry_month" maxlength="2" placeholder="MM" />
<input type="text" name="expiry_year" id="expiry_year" maxlength="2" placeholder="YY" />
</li>
<li>
<label>CVV</label>
<input type="text" name="cvv" id="cvv" maxlength="3" placeholder="123" />
</li>
</ul>
</li>
<li>
<input type="submit" id="paymentButton" value="Proceed" disabled="true" class="disable">
</li>
</ul>
</form>
<div id="orderInfo"></div>
CSS Code
*{margin: 0px; padding:0px;}
ul {list-style: none;}
label{padding:8px 0px 8px 0px;}
#paymentForm label
{
color: #555;
display: block;
font-size: 14px;
font-weight: 400;
}
#paymentForm input[type=text]
{
background-color: #FFFFFF;
border: 1px solid #E5E5E5;
color: #333333;
display: block;
font-size: 18px;
height: 32px;
padding: 0 5px;
width: 275px;
}
#paymentForm li {margin: 8px 0;}
#orderInfo{display:none}
.disable{opacity: 0.2}
.vertical li
{
float: left;
width: 140px;
}
ul {list-style: none;}
label{padding:8px 0px 8px 0px;}
#paymentForm label
{
color: #555;
display: block;
font-size: 14px;
font-weight: 400;
}
#paymentForm input[type=text]
{
background-color: #FFFFFF;
border: 1px solid #E5E5E5;
color: #333333;
display: block;
font-size: 18px;
height: 32px;
padding: 0 5px;
width: 275px;
}
#paymentForm li {margin: 8px 0;}
#orderInfo{display:none}
.disable{opacity: 0.2}
.vertical li
{
float: left;
width: 140px;
}
JavaScript
Validating the payment form using Regular Expressions.
<script src="js/jquery.min.js"></script>
<script src="js/jquery.creditCardValidator.js"></script>
<script>
$(document).ready(function()
{
/* Form Validation */
$("#paymentForm input[type=text]").on("keyup",function()
{
var cardValid=$("#card_number").attr('rel');
var C=$("#card_name").val();
var M=$("#expiry_month").val();
var Y=$("#expiry_year").val();
var CVV=$("#cvv").val();
var expName =/^[a-z ,.'-]+$/i;
var expMonth = /^01|02|03|04|05|06|07|08|09|10|11|12$/;
var expYear = /^16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31$/;
var expCVV=/^[0-9]{3,3}$/;
if(cardValid>0 && expName.test(C) && expMonth.test(M) && expYear.test(Y)
&& expCVV.test(CVV) && parseInt(cardCheck)>0)
{
$('#paymentButton').prop('disabled', false);
$('#paymentButton').removeClass('disable');
}
else
{
$('#paymentButton').prop('disabled', true);
$('#paymentButton').addClass('disable');
}
});
/* Card Validation */
cardValidate();
/*Payment Form */
$("#paymentForm").submit(function()
{
//.......
});
});
</script>
<script src="js/jquery.creditCardValidator.js"></script>
<script>
$(document).ready(function()
{
/* Form Validation */
$("#paymentForm input[type=text]").on("keyup",function()
{
var cardValid=$("#card_number").attr('rel');
var C=$("#card_name").val();
var M=$("#expiry_month").val();
var Y=$("#expiry_year").val();
var CVV=$("#cvv").val();
var expName =/^[a-z ,.'-]+$/i;
var expMonth = /^01|02|03|04|05|06|07|08|09|10|11|12$/;
var expYear = /^16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31$/;
var expCVV=/^[0-9]{3,3}$/;
if(cardValid>0 && expName.test(C) && expMonth.test(M) && expYear.test(Y)
&& expCVV.test(CVV) && parseInt(cardCheck)>0)
{
$('#paymentButton').prop('disabled', false);
$('#paymentButton').removeClass('disable');
}
else
{
$('#paymentButton').prop('disabled', true);
$('#paymentButton').addClass('disable');
}
});
/* Card Validation */
cardValidate();
/*Payment Form */
$("#paymentForm").submit(function()
{
//.......
});
});
</script>
Card Validation
Credit Card Validation using jquery.validateCreditCard plugin.
/* Credit Card Type Check */
function cardValidate()
{
$('#card_number').validateCreditCard(function(result)
{
var N=$(this).val();
var C=$(this).attr("class");
$(this).attr("class","");
if(result && N.length>0)
{
$(this).addClass(result.card_type.name);
if(result.valid && result.length_valid && result.luhn_valid)
{
$(this).addClass('valid');
$(this).attr("rel","1");
}
else
{
$(this).attr("rel","1");
}
}
else
{
$(this).removeClass(C);
}
});
}
function cardValidate()
{
$('#card_number').validateCreditCard(function(result)
{
var N=$(this).val();
var C=$(this).attr("class");
$(this).attr("class","");
if(result && N.length>0)
{
$(this).addClass(result.card_type.name);
if(result.valid && result.length_valid && result.luhn_valid)
{
$(this).addClass('valid');
$(this).attr("rel","1");
}
else
{
$(this).attr("rel","1");
}
}
else
{
$(this).removeClass(C);
}
});
}
Ajax Payment Process
BrainTree Payment process with Jquery Ajax, here $(this).serialize() get all the form input values and posting to cardProcess.php. Once order is successful, use will get the Order status message.
/*Payment Form */
$("#paymentForm").submit(function()
{
var datastring = $(this).serialize();
$.ajax({
type: "POST",
url: "cardProcess.php",
data: datastring,
dataType: "json",
beforeSend: function(){ $("#paymentButton").val('Processing..') },
success: function(data)
{
$.each(data.OrderStatus, function(i,data)
{
var HTML;
if(data)
{
$("#paymentGrid").slideUp("slow");
$("#orderInfo").fadeIn("slow");
if(data.status == '1')
{
HTML="Order <span>"+data.orderID+"</span> has been created successfully.";
}
else if(data.status == '2')
{
HTML="Transaction has been failed, please use other card.";
}
else
{
HTML="Card number is not valid, please use other card.";
}
$("#orderInfo").html(HTML);
}
});
},
error: function(){ alert('Network Error'); }
});
return false;
});
$("#paymentForm").submit(function()
{
var datastring = $(this).serialize();
$.ajax({
type: "POST",
url: "cardProcess.php",
data: datastring,
dataType: "json",
beforeSend: function(){ $("#paymentButton").val('Processing..') },
success: function(data)
{
$.each(data.OrderStatus, function(i,data)
{
var HTML;
if(data)
{
$("#paymentGrid").slideUp("slow");
$("#orderInfo").fadeIn("slow");
if(data.status == '1')
{
HTML="Order <span>"+data.orderID+"</span> has been created successfully.";
}
else if(data.status == '2')
{
HTML="Transaction has been failed, please use other card.";
}
else
{
HTML="Card number is not valid, please use other card.";
}
$("#orderInfo").html(HTML);
}
});
},
error: function(){ alert('Network Error'); }
});
return false;
});
BrainTree Integration
Create a sandbox account at sandbox.braintreegateway.com for testing process. Go to Account -> My User scroll down you will find API keys.
You will find merchantId, publicKey and privateKey
Here you have to modify with your BrainTree merchandID, pulicKey and privateKey. This is update Orders table, once the transaction is successful. Always maintain/store cart total price value in session. Once testing is done, you can modify environment value from sandbox to production.
<?php
include 'db.php';
include 'functions.php';
$session_price =$_SESSION['session_price']; //Cart Total Price
if($_SERVER["REQUEST_METHOD"] == "POST" )
{
$card_number=str_replace("+","",$_POST['card_number']);
$card_name=$_POST['card_number'];
$expiry_month=$_POST['expiry_month'];
$expiry_year=$_POST['expiry_year'];
$cvv=$_POST['cvv'];
$expirationDate=$expiry_month.'/'.$expiry_year;
require_once 'braintree/Braintree.php';
Braintree_Configuration::environment('sandbox'); // Change to production
Braintree_Configuration::merchantId('Merchant_ID');
Braintree_Configuration::publicKey('Public_Key');
Braintree_Configuration::privateKey('Private_Key');
//BrainTree payment process
$result = Braintree_Transaction::sale(array(
'amount' => $price,
'creditCard' => array(
'number' => $card_number,
'cardholderName' => $card_name,
'expirationDate' => $expirationDate,
'cvv' => $cvv
)
));
if ($result->success)
{
if($result->transaction->id)
{
$braintreeCode=$result->transaction->id;
updateUserOrder($braintreeCode,$session_user_id,$session_price); //Order table update.
}
}
else if ($result->transaction)
{
echo '{"OrderStatus": [{"status":"2"}]}';
}
else
{
echo '{"OrderStatus": [{"status":"0"}]}';
}
}
?>
include 'db.php';
include 'functions.php';
$session_price =$_SESSION['session_price']; //Cart Total Price
if($_SERVER["REQUEST_METHOD"] == "POST" )
{
$card_number=str_replace("+","",$_POST['card_number']);
$card_name=$_POST['card_number'];
$expiry_month=$_POST['expiry_month'];
$expiry_year=$_POST['expiry_year'];
$cvv=$_POST['cvv'];
$expirationDate=$expiry_month.'/'.$expiry_year;
require_once 'braintree/Braintree.php';
Braintree_Configuration::environment('sandbox'); // Change to production
Braintree_Configuration::merchantId('Merchant_ID');
Braintree_Configuration::publicKey('Public_Key');
Braintree_Configuration::privateKey('Private_Key');
//BrainTree payment process
$result = Braintree_Transaction::sale(array(
'amount' => $price,
'creditCard' => array(
'number' => $card_number,
'cardholderName' => $card_name,
'expirationDate' => $expirationDate,
'cvv' => $cvv
)
));
if ($result->success)
{
if($result->transaction->id)
{
$braintreeCode=$result->transaction->id;
updateUserOrder($braintreeCode,$session_user_id,$session_price); //Order table update.
}
}
else if ($result->transaction)
{
echo '{"OrderStatus": [{"status":"2"}]}';
}
else
{
echo '{"OrderStatus": [{"status":"0"}]}';
}
}
?>
Update User Order
This will update orders table, based on the BrainTree status code. Cart table status will change 0 to 1 with order id.
<?php
function updateUserOrder($braintreeCode,$session_user_id,$session_price)
{
$db = getDB();
$sql = "INSERT INTO Orders(user_id_fk,created,braintreeCode)VALUES(:user_id,:created,:braintreeCode)";
$stmt = $db->prepare($sql);
$stmt->bindParam("user_id", $session_user_id);
$time=time();
$stmt->bindParam("created", $time);
$stmt->bindParam("braintreeCode", $braintreeCode);
$stmt->execute();
$sql1 = "SELECT order_id FROM Orders WHERE user_id_fk=:user_id ORDER BY order_id DESC LIMIT 1";
$stmt1 = $db-> prepare($sql1);
$stmt1-> bindParam("user_id", $session_user_id);
$stmt1-> execute();
$OrderDetails = $stmt1->fetchAll(PDO::FETCH_OBJ);
$order_id=$OrderDetails[0]->order_id;
$sql2 = "UPDATE Cart SET order_id_fk=:order_id,cart_status='1',price=:price WHERE cart_status='0' AND user_id_fk=:user_id";
$stmt2 = $db-> prepare($sql2);
$stmt2-> bindParam("user_id", $session_user_id);
$stmt2-> bindParam("order_id", $order_id);
$stmt2-> bindParam("price", $session_price);
$stmt2-> execute();
$db = null;
echo '{"OrderStatus": [{"status":"1", "orderID":"'.$order_id.'"}]}';
}
?>
function updateUserOrder($braintreeCode,$session_user_id,$session_price)
{
$db = getDB();
$sql = "INSERT INTO Orders(user_id_fk,created,braintreeCode)VALUES(:user_id,:created,:braintreeCode)";
$stmt = $db->prepare($sql);
$stmt->bindParam("user_id", $session_user_id);
$time=time();
$stmt->bindParam("created", $time);
$stmt->bindParam("braintreeCode", $braintreeCode);
$stmt->execute();
$sql1 = "SELECT order_id FROM Orders WHERE user_id_fk=:user_id ORDER BY order_id DESC LIMIT 1";
$stmt1 = $db-> prepare($sql1);
$stmt1-> bindParam("user_id", $session_user_id);
$stmt1-> execute();
$OrderDetails = $stmt1->fetchAll(PDO::FETCH_OBJ);
$order_id=$OrderDetails[0]->order_id;
$sql2 = "UPDATE Cart SET order_id_fk=:order_id,cart_status='1',price=:price WHERE cart_status='0' AND user_id_fk=:user_id";
$stmt2 = $db-> prepare($sql2);
$stmt2-> bindParam("user_id", $session_user_id);
$stmt2-> bindParam("order_id", $order_id);
$stmt2-> bindParam("price", $session_price);
$stmt2-> execute();
$db = null;
echo '{"OrderStatus": [{"status":"1", "orderID":"'.$order_id.'"}]}';
}
?>
Get User Cart Details
Getting card details based on user session id.
<?php
function getUserCartDetails($session_user_id)
{
$db = getDB();
$sql = "SELECT P.product_name,P.price FROM Users U, Cart C, Products P WHERE U.user_id=C.user_id_fk AND P.product_id = C.product_id_fk AND C.user_id_fk=:user_id AND C.cart_status='0'";
$stmt = $db->prepare($sql);
$stmt->bindParam("user_id", $session_user_id);
$stmt->execute();
$getUserCartDetails = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
return $getUserCartDetails;
}
?>
function getUserCartDetails($session_user_id)
{
$db = getDB();
$sql = "SELECT P.product_name,P.price FROM Users U, Cart C, Products P WHERE U.user_id=C.user_id_fk AND P.product_id = C.product_id_fk AND C.user_id_fk=:user_id AND C.cart_status='0'";
$stmt = $db->prepare($sql);
$stmt->bindParam("user_id", $session_user_id);
$stmt->execute();
$getUserCartDetails = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
return $getUserCartDetails;
}
?>
Live Demo
db.php
You have to modify username, password and databaseName values. Make sure enable PDO extension in PHP.ini.
<?php
function getDB() {
$dbhost="localhost";
$dbuser="username";
$dbpass="password";
$dbname="databaseName";
$dbConnection = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbConnection;
}
/* User Sessions */
session_start();
$session_user_id=$_SESSION['user_id'];
function getDB() {
$dbhost="localhost";
$dbuser="username";
$dbpass="password";
$dbname="databaseName";
$dbConnection = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbConnection;
}
/* User Sessions */
session_start();
$session_user_id=$_SESSION['user_id'];
Note: Once your testing is done, create a BrainTree production account and modify the key details.
Thank Srinivas Tamada!
ReplyDeleteNice article, it's useful with me.
Kevin.
great post!
ReplyDeleteYou always post what developers needs in everyday development. It will be great if you cover all the payment gateways. specially paypal recurring.
ReplyDeleteThank you for this post.
ReplyDeleteYour post really help me out for the payment integration of braintree on my custom php script. Thanks a ton.
ReplyDeletethanks for this great effort
ReplyDeletethanks very much, its most important for developer, i really need some idea about payment for e-commerce since a week, but with good luck and help all of you i find it
ReplyDeletethanks a lot
Thanks a lot sir. I am appreciating your approach. This is really important for developers. Your post is always inspiring me, to love our jobs. i am working as Sr. PHP Developer in a small company. This code is very helpful for me and other developer. Thanks a lot once again.
ReplyDelete@Praveen Kumar Thanks a lot for such a nice comment. :)
DeleteHI Srinivas Tamada
DeleteAm searching this script on today.am so lucky..i got script from u.i didn't expect .thanks a lot
Excelente, muchas gracias
ReplyDeleteThanks very much for your clear and comprehensive guide.
ReplyDeleteHowever, I am having some issues with processing the payment:
GET https://orderin24.es/images/macbook.png 404 (Not Found)
jquery.min.js:4 POST https://orderin24.es/cardProcess.php 500 (Internal Server Error)n.ajaxTransport.k.cors.a.crossDomain.send @ jquery.min.js:4n.extend.ajax @ jquery.min.js:4(anonymous function) @ card.js:71n.event.dispatch @ jquery.min.js:3n.event.add.r.handle @ jquery.min.js:3
''error handling here''
create a column 'braintreeCode' in order table.
DeleteHi,
ReplyDeleteI had read your article and you have described it really very well. But I have one question is there this system will be trustful to user as its creating payment processing form in own portal. As usual we are sending user to paypal site and there to feel safe to purchase. I am just confused is there the idea to use payment system on own site is good or to send user to payment getway provider page is safe ?
Thanks for your great article.
Hello, I have downloaded your code, input the sql, however when I click the green Proceed button, it gives me a "error handling here" alert. What may be the reason for this?
ReplyDeleteThank you for this code and I am waiting for your answer!
Check Ajax output.
DeleteWere you able to solve this ANONYMOUS? Your question on october 17 at 8:32 is the same one that I have. I am not sure where to check the Ajax output or what I am looking for in this case?
Deletevery good info.
ReplyDeleteNice!!! Tamada
ReplyDeleteHope paypal doesn't limit the account again, which it has done a couple of times before to me. @Srinivas it would be very nice of you if you can guide for direct payment on a site like credit card, or other online payment processing
ReplyDeleteIs there any demo braintree with paypal express checkout?
ReplyDeleteNice Post...
ReplyDeleteHi, Where have you created client token ?
ReplyDeleteits not taking session price value.. please suggest , issue with single quotes
ReplyDeletesir when m downloding the script ...it tells CONFIDENTIAL
ReplyDeleteThis document contains sensitive information. how to resolve this.
You mean while downloading?
DeleteI am downloading your code and perfect work for me (my site and also my local machine).
ReplyDeleteThanks,
May i know where your using client token and payment_method_nonce?
ReplyDeleteNice tutorial :) do you have any examples using the node.js set up? Many thanks
ReplyDeleteNice tutorial :-) I get the same error : "error handling here"
ReplyDeleteI checked the jQuery output and it gives : jquery.min.js:4 POST http://localhost/braintree/cardProcess.php 500 (Internal Server Error)
Any help is appreciated
there are alot of conflicts in the code you have displayed here and the code that you provide for download, please sync the code.
ReplyDeletehi, i am also getting same error: "error handling here" ../braintree/cardProcess.php 500 (Internal Server Error).
ReplyDeleteAny Help..
Not really true if anyone is really debugging the code, but I found the the line 9 in cardProcess.php is having problem. It is provided a wrong value.
ReplyDeleteThank you so much for this! I have successfully implemented with my project but just one issue. I would like to redirect the user to a different page after the database has been updated. Does anyone know how I would do this? Location: did not work here. Thanks in advance.
ReplyDeleteGreat tutorials but I think we should not send card detail to our server.
ReplyDeleteYes and it depends on user terms.
DeleteGreat well presented article, thank you
ReplyDeletethank you very much
ReplyDeletePerfect Script
Perfect
ReplyDeleteThank you
Hello Sir
ReplyDeletei noticed there is big mistake you are passing card_number in the field instead of card holder name.
we have recieved this Compliance Violation from Braintree.
Great one article ! Thanks brother to post which is full of information.
ReplyDeleteHi Srinivas, I went through the post and it is really very helpful.
ReplyDeleteHow to prepopulate used payment methods to user?
hi sir how to live mode in braintree
ReplyDeleteBraintree_Configuration::environment('sandbox');
Deleteto
Braintree_Configuration::environment('production');
I would rather validate Credit Card Numbers SeverSide sicne you can easily manipulate clinet sie code ;)
ReplyDeleteThanks very much for your clear and comprehensive guide.
ReplyDeleteHowever, I am having some issues with processing the payment:
Card number is not valid, please use other card.
even with my own card
Can you help me please
SSL certificate issue EXCEPTION IN localhost
ReplyDeletethere's a problem with download button,
ReplyDeletethe url should be https..... instead of http.....
Hello Sir, i used sandbox and everything worked fine. I created a production account but it does not contain the api keys(public and private) it only contains the merchant id. Any help?
ReplyDelete