diff --git a/README.md b/README.md
index 3a64cd0..d4ce6c4 100755
--- a/README.md
+++ b/README.md
@@ -1,76 +1,89 @@
# Accept Sample Application
+
The Accept sample application provides examples of how to use the Authorize.Net Accept suite of tools to integrate secure payment acceptance into your applications.
+> ### ⚠️ Scope & Intended Use
+>
+> This repository is a **sample / educational application** — it is **not a product**, **not a hosted service**, and **not part of the main Authorize.Net product**. It is intended for developers who want to clone, host locally, and learn how the Accept suite (Accept.js, Accept Hosted, Accept Customer / Hosted CIM, Apple Pay on the Web) integrates against the Authorize.Net **sandbox**.
+>
+> - **Purpose:** Educational reference / integration showcase only.
+> - **Deployment:** Designed to be hosted **locally** by each developer (IIS / XAMPP / Apache) over HTTPS. It is _not_ deployed to any hosted environment by Authorize.Net or Visa.
+> - **Backend target:** Authorize.Net **Sandbox only** (`https://apitest.authorize.net`); Apple Pay **Sandbox only** (`apple-pay-gateway-cert.apple.com`). There is no production URL anywhere in the codebase.
+> - **Data:** No database. State is per-PHP-process `$_SESSION` only.
+> - **Credentials:** All Authorize.Net credentials come from per-developer-machine **environment variables** (`API_LOGIN_ID`, `TRANSACTION_KEY`, `PEM_PWD`, `DOMAIN_NAME`, `MERCHANT_ID`). The Accept.js public client key is replaced by the developer in `acceptJSCaller.js`. No real credentials ship in source.
+>
+> 📄 **Full security context, threat model, and AI SAST `poc_personal` validation guidance:** see [`SECURITY-CONTEXT.md`](./SECURITY-CONTEXT.md).
+

## How to Use the Sample App
-+ Clone or download this repository.
+- Clone or download this repository.
-+ Host the sample app in any web server supporting PHP, for example IIS or XAMPP (Apache). __HTTPS (SSL) must be enabled for your website.__
+- Host the sample app in any web server supporting PHP, for example IIS or XAMPP (Apache). **HTTPS (SSL) must be enabled for your website.**
-+ Set the authentication credentials in the application so that it uses your Authorize.Net sandbox (test) account. If you haven't yet signed up for a sandbox account, you can create a sandbox account at our [Developer Center] (https://developer.authorize.net/hello_world/sandbox/). Set environment variables for `API_LOGIN_ID` and `TRANSACTION_KEY` using the credentials for your Authorize.Net sandbox account. For example, in _httpd.conf_, you would add the following lines:
+- Set the authentication credentials in the application so that it uses your Authorize.Net sandbox (test) account. If you haven't yet signed up for a sandbox account, you can create a sandbox account at our [Developer Center] (https://developer.authorize.net/hello_world/sandbox/). Set environment variables for `API_LOGIN_ID` and `TRANSACTION_KEY` using the credentials for your Authorize.Net sandbox account. For example, in _httpd.conf_, you would add the following lines:
SetEnv API_LOGIN_ID your_id
- SetEnv TRANSACTION_KEY your_key
+ SetEnv TRANSACTION_KEY your_key
For IIS, you could set these environment variables in FastCGI Settings -> Environment Variables.
-+ Set the authentication credentials that Accept.js uses. Edit the `acceptJSCaller()` function in _acceptJSCaller.js_ to use your API login ID and public client key for the values of `authData.apiLoginID` and `authData.clientKey`. A public client key can be created by logging into the [Merchant Interface](https://sandbox.authorize.net/) and navigating to __Account --> Security Settings --> Manage Public Client Key__.
+- Set the authentication credentials that Accept.js uses. Edit the `acceptJSCaller()` function in _acceptJSCaller.js_ to use your API login ID and public client key for the values of `authData.apiLoginID` and `authData.clientKey`. A public client key can be created by logging into the [Merchant Interface](https://sandbox.authorize.net/) and navigating to **Account --> Security Settings --> Manage Public Client Key**.
-+ Browse the application (_index.php_) over HTTPS connection.
+- Browse the application (_index.php_) over HTTPS connection.
-+ To "log in", use a customer profile ID that already exists within your account or create a new one by using the [Create a Customer Profile API](http://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile).
+- To "log in", use a customer profile ID that already exists within your account or create a new one by using the [Create a Customer Profile API](http://developer.authorize.net/api/reference/index.html#customer-profiles-create-customer-profile).
-+ In these examples, payment forms are shown in the same page and Shipping forms are handled in a separate modal popup. However, any of the types of display can be chosen to display any type of form.
+- In these examples, payment forms are shown in the same page and Shipping forms are handled in a separate modal popup. However, any of the types of display can be chosen to display any type of form.
### Common issues during installation
-1. **Error**: *Uncaught Error: Class 'SimpleXMLElement' not found in accept-sample-app/getToken.php:24*
-+ **Possible methods to resolve**
- - Install simplexml package for your OS. Refer: http://stackoverflow.com/questions/35593521/php-7-simplexml
-+ **Related issue**
- - https://github.com/AuthorizeNet/accept-sample-app/issues/25
+1. **Error**: _Uncaught Error: Class 'SimpleXMLElement' not found in accept-sample-app/getToken.php:24_
+
+- **Possible methods to resolve** - Install simplexml package for your OS. Refer: http://stackoverflow.com/questions/35593521/php-7-simplexml
+- **Related issue** - https://github.com/AuthorizeNet/accept-sample-app/issues/25
## Included Examples
### Accept Customer
-Accept Customer is the new name for Hosted CIM, part of our Customer Profiles API. See our [developer documentation](http://developer.authorize.net/api/reference/features/customer_profiles.html) for more details.
-The sample application shows how to:
+Accept Customer is the new name for Hosted CIM, part of our Customer Profiles API. See our [developer documentation](http://developer.authorize.net/api/reference/features/customer_profiles.html) for more details.
-* Incorporate the Manage Customer hosted page into your application ("Profile" tab).
-* Embed the hosted "Add/Edit Payment" page into your application as an iframe ("Payment Methods" tab).
-* Pop up the hosted "Add/Edit Shipping" page in a light-box mode ("Shipping" tab).
+The sample application shows how to:
+- Incorporate the Manage Customer hosted page into your application ("Profile" tab).
+- Embed the hosted "Add/Edit Payment" page into your application as an iframe ("Payment Methods" tab).
+- Pop up the hosted "Add/Edit Shipping" page in a light-box mode ("Shipping" tab).
### Accept.js
-Accept.js is a new integration option which allows you to leverage the full power of the Authorize.Net API while avoiding the PCI compliance burden of credit card information hitting your servers. See our [developer documentation](http://developer.authorize.net/api/reference/features/acceptjs.html) for more details.
-The sample application shows how to:
+Accept.js is a new integration option which allows you to leverage the full power of the Authorize.Net API while avoiding the PCI compliance burden of credit card information hitting your servers. See our [developer documentation](http://developer.authorize.net/api/reference/features/acceptjs.html) for more details.
-* Incorporate the Accept.js library into your existing payment flow ("Pay" tab --> "Pay (Accept.js)" button)
+The sample application shows how to:
+- Incorporate the Accept.js library into your existing payment flow ("Pay" tab --> "Pay (Accept.js)" button)
### Accept Hosted
-Accept Hosted provides a fully hosted payment transaction solution. Authorize.Net takes care of the payment form, the transaction itself, and the receipt generation (optional). We have a Step-by-Step guide to the sample implementation here: [README-AcceptHosted.md](README-AcceptHosted.md). See our [developer documentation](http://developer.authorize.net/api/reference/features/accept_hosted.html) for more details.
-The sample application shows how to:
+Accept Hosted provides a fully hosted payment transaction solution. Authorize.Net takes care of the payment form, the transaction itself, and the receipt generation (optional). We have a Step-by-Step guide to the sample implementation here: [README-AcceptHosted.md](README-AcceptHosted.md). See our [developer documentation](http://developer.authorize.net/api/reference/features/accept_hosted.html) for more details.
-* Request an Accept Hosted form token using the Authorize.Net API (GetHostedPaymentForm).
-* Incorporate the Accept Hosted payment form into your existing payment flow ("Pay" tab).
-* Display a custom receipt using the transaction response.
+The sample application shows how to:
+- Request an Accept Hosted form token using the Authorize.Net API (GetHostedPaymentForm).
+- Incorporate the Accept Hosted payment form into your existing payment flow ("Pay" tab).
+- Display a custom receipt using the transaction response.
### Apple Pay On The Web
-Authorize.Net supports Apple Pay on the Web in addition to our in-app Apple Pay Support. See our [developer documentation](http://developer.authorize.net/api/reference/features/in-app.html) for more details.
+
+Authorize.Net supports Apple Pay on the Web in addition to our in-app Apple Pay Support. See our [developer documentation](http://developer.authorize.net/api/reference/features/in-app.html) for more details.

-In this sample we demonstrate how to:
+In this sample we demonstrate how to:
-* Integrate with the ApplePay.js library.
-* Validate your merchant identity from your server.
-* Complete the transaction by passing the Apple Pay payment data in the Authorize.Net `createTransactionRequest` API call.
+- Integrate with the ApplePay.js library.
+- Validate your merchant identity from your server.
+- Complete the transaction by passing the Apple Pay payment data in the Authorize.Net `createTransactionRequest` API call.
Please note that you will need to have a merchant ID set up with Apple as described in their [Apple Pay documentation](https://developer.apple.com/reference/applepayjs/).
diff --git a/acceptJSCaller.js b/acceptJSCaller.js
index 22febb0..745ad64 100644
--- a/acceptJSCaller.js
+++ b/acceptJSCaller.js
@@ -1,18 +1,20 @@
// The result of the transaction processing will be returned from the processing script as a JSON object. Parse the object to determine success or failure, and alert the user.
-function messageFunc(msg)
-{
- try{
- responseObj=JSON.parse(msg);
- if(responseObj.transactionResponse.responseCode=='1'){
- message="Transaction Successful! Transaction ID: "+responseObj.transactionResponse.transId;
- }
- else{
- message="Transaction Unsuccessful.";//+responseObj.messages.message[0].text;
- if(responseObj.transactionResponse.errors!=null)//to do: take care of errors[1] array being parsed into single object
- {
- message+=responseObj.transactionResponse.errors.error.errorText;
- }
- /*else if(responseObj.transactionResponse.errors[0]!=null)
+function messageFunc(msg) {
+ try {
+ responseObj = JSON.parse(msg);
+ if (responseObj.transactionResponse.responseCode == "1") {
+ message =
+ "Transaction Successful! Transaction ID: " +
+ responseObj.transactionResponse.transId;
+ } else {
+ message = "Transaction Unsuccessful."; //+responseObj.messages.message[0].text;
+ if (
+ responseObj.transactionResponse.errors != null
+ ) //to do: take care of errors[1] array being parsed into single object
+ {
+ message += responseObj.transactionResponse.errors.error.errorText;
+ }
+ /*else if(responseObj.transactionResponse.errors[0]!=null)
{
for(i=0;i";
- message+=("Transaction ID: "+responseObj.transactionResponse.transId)
- }
- }
- }
- catch(error){
- console.log("Couldn't parse result string");
- message="Error.";
- }
-
- //alert(message);
-
- $('#acceptJSReceiptBody').html(message);
- //jQuery.noConflict();
- $('#acceptJSPayModal').modal('hide');
- $('#acceptJSReceiptModal').modal('show');
+ if (responseObj.transactionResponse.transId != null) {
+ message += " ";
+ message += "Transaction ID: " + responseObj.transactionResponse.transId;
+ }
+ }
+ } catch (error) {
+ console.log("Couldn't parse result string");
+ message = "Error.";
+ }
+
+ //alert(message);
+
+ $("#acceptJSReceiptBody").html(message);
+ //jQuery.noConflict();
+ $("#acceptJSPayModal").modal("hide");
+ $("#acceptJSReceiptModal").modal("show");
}
// Do an AJAX call to submit the transaction data and the payment none to a separate PHP page to do the actual transaction processing.
function createTransact(dataObj) {
-
- // Set Amount for demo purposes if not set by callers form
- myAmt = document.getElementById('amount').value;
- if(!myAmt)
- {
- myAmt = Math.floor((Math.random() * 100) + 1);
- }
- console.log('Amount = '+myAmt);
-
- $.ajax({
-
- url: "transactionCaller.php",
- data: {amount: myAmt, dataDesc: dataObj.dataDescriptor, dataValue: dataObj.dataValue},
- method: 'POST',
- timeout: 5000
-
- }).done(function(data){
-
- console.log('Success');
-
- }).fail(function(){
-
- console.log('Error');
-
- }).always(function(textStatus){
-
- console.log(textStatus);
- messageFunc(textStatus);
-
- })
-
+ // Set Amount for demo purposes if not set by callers form
+ myAmt = document.getElementById("amount").value;
+ if (!myAmt) {
+ myAmt = Math.floor(Math.random() * 100 + 1);
+ }
+ console.log("Amount = " + myAmt);
+
+ // Security: Get CSRF token from hidden input
+ var csrfToken = $("#csrfToken").val();
+
+ $.ajax({
+ url: "transactionCaller.php",
+ data: {
+ amount: myAmt,
+ dataDesc: dataObj.dataDescriptor,
+ dataValue: dataObj.dataValue,
+ csrf_token: csrfToken, // Security: Include CSRF token
+ },
+ method: "POST",
+ timeout: 5000,
+ })
+ .done(function (data) {
+ console.log("Success");
+ })
+ .fail(function () {
+ console.log("Error");
+ })
+ .always(function (textStatus) {
+ console.log(textStatus);
+ messageFunc(textStatus);
+ });
}
// Process the response from Authorize.Net to retrieve the two elements of the payment nonce.
// If the data looks correct, record the OpaqueData to the console and call the transaction processing function.
-function responseHandler(response) {
- if (response.messages.resultCode === 'Error') {
- for (var i = 0; i < response.messages.message.length; i++) {
- console.log(response.messages.message[i].code + ':' + response.messages.message[i].text);
- }
- alert("acceptJS library error!")
- } else {
- console.log(response.opaqueData.dataDescriptor);
- console.log(response.opaqueData.dataValue);
- createTransact(response.opaqueData);
- }
+function responseHandler(response) {
+ if (response.messages.resultCode === "Error") {
+ for (var i = 0; i < response.messages.message.length; i++) {
+ console.log(
+ response.messages.message[i].code +
+ ":" +
+ response.messages.message[i].text,
+ );
+ }
+ alert("acceptJS library error!");
+ } else {
+ console.log(response.opaqueData.dataDescriptor);
+ console.log(response.opaqueData.dataValue);
+ createTransact(response.opaqueData);
+ }
}
-function acceptJSCaller()
-{
- var secureData = {} , authData = {} , cardData = {};
-
- // Extract the card number and expiration date.
- cardData.cardNumber = document.getElementById('creditCardNumber').value;
- cardData.cardCode = document.getElementById('cvv').value;
- cardData.month = document.getElementById('expiryDateMM').value;
- cardData.year = document.getElementById('expiryDateYY').value;
- secureData.cardData = cardData;
+function acceptJSCaller() {
+ var secureData = {},
+ authData = {},
+ cardData = {};
+
+ // Extract the card number and expiration date.
+ cardData.cardNumber = document.getElementById("creditCardNumber").value;
+ cardData.cardCode = document.getElementById("cvv").value;
+ cardData.month = document.getElementById("expiryDateMM").value;
+ cardData.year = document.getElementById("expiryDateYY").value;
+ secureData.cardData = cardData;
- // The Authorize.Net Client Key is used in place of the traditional Transaction Key. The Transaction Key
- // is a shared secret and must never be exposed. The Client Key is a public key suitable for use where
- // someone outside the merchant might see it.
+ // SAMPLE ONLY:
+ // `apiLoginID` is a non-secret merchant identifier (NOT a credential like
+ // `Transaction Key`). `clientKey` is the Authorize.Net Public Client Key —
+ // it is designed to be exposed to the browser; it can ONLY be used by the
+ // issuing merchant's own Accept.js flow to mint single-use, short-lived
+ // opaque tokens for that merchant's own card data.
+ //
+ // Per README, each developer must replace these placeholders with their
+ // own per-developer sandbox values. Generate them in the Authorize.Net
+ // Sandbox Merchant Interface → Account → Security Settings →
+ // - "API Credentials & Keys" for the apiLoginID
+ // - "Manage Public Client Key" for the clientKey
+ //
+ // The shared-secret `Transaction Key` (which IS a credential) lives only
+ // in your server-side env-vars (`getenv("TRANSACTION_KEY")` in
+ // transactionCaller.php) and is never sent to the browser.
+ authData.clientKey = "[YOUR_PUBLIC_CLIENT_KEY]";
+ authData.apiLoginID = "[YOUR_API_LOGIN_ID]";
+ secureData.authData = authData;
- authData.clientKey = '6jZy4G5vmCEat9G3xjtNguj7DLw5NhgS4PBr4KNp7tV2tXa34E3BkdG33dcX4S84';
- authData.apiLoginID = '3e3b5H4YLP';
- secureData.authData = authData;
-
- // Pass the card number and expiration date to Accept.js for submission to Authorize.Net.
- Accept.dispatchData(secureData, 'responseHandler');
+ // Pass the card number and expiration date to Accept.js for submission to Authorize.Net.
+ Accept.dispatchData(secureData, "responseHandler");
}
diff --git a/applePayCaller.js b/applePayCaller.js
index b75d8ea..300e864 100644
--- a/applePayCaller.js
+++ b/applePayCaller.js
@@ -1,138 +1,138 @@
if (window.ApplePaySession) {
- var merchantIdentifier = 'ApplePayDemoTestDev15';
- var promise = ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier);
- promise.then( function (canMakePayments) {
- if (canMakePayments){
- console.log("Apple Pay Payment Available");
- $("#applePayButton").prop('disabled', false);
- }else{
- console.log("Apple Pay is available but not activated yet");
- }
- });
-}
-else{
- console.log("Apple Pay not available in this browser");
+ var merchantIdentifier = "ApplePayDemoTestDev15";
+ var promise =
+ ApplePaySession.canMakePaymentsWithActiveCard(merchantIdentifier);
+ promise.then(function (canMakePayments) {
+ if (canMakePayments) {
+ console.log("Apple Pay Payment Available");
+ $("#applePayButton").prop("disabled", false);
+ } else {
+ console.log("Apple Pay is available but not activated yet");
+ }
+ });
+} else {
+ console.log("Apple Pay not available in this browser");
}
function createTransaction(dataObj) {
-
- console.log('starting createTransaction');
- console.log(dataObj);
-
- let objJsonStr = JSON.stringify(dataObj);
- console.log(objJsonStr);
- let objJsonB64 = window.btoa(objJsonStr);
- console.log(objJsonB64);
-
- $.ajax({
-
- url: "transactionCaller.php",
- data: {amount: '15.00', dataDesc: 'COMMON.APPLE.INAPP.PAYMENT', dataValue: objJsonB64},
- method: 'POST',
- timeout: 5000
-
- }).done(function(data){
- console.log(data);
- console.log('Success');
-
- }).fail(function(){
-
- console.log('Error');
- })
-
- return true;
-}
+ console.log("starting createTransaction");
+ console.log(dataObj);
-function applePayButtonClicked(){
-
- console.log('Apple Pay Initiated');
-
- var request = {
- countryCode: 'US',
- currencyCode: 'USD',
- supportedNetworks: ['visa', 'masterCard', 'amex', 'discover'],
- merchantCapabilities: ['supports3DS','supportsCredit', 'supportsDebit'], // Make sure NOT to include supportsEMV here
- total: { label: 'Test Spices', amount: '15.00' },
- }
-
- var session = new ApplePaySession(1, request);
-
- // Merchant Validation
- session.onvalidatemerchant = function (event) {
- console.log(event);
- var promise = performValidation(event.validationURL);
- promise.then(function (merchantSession) {
- session.completeMerchantValidation(merchantSession);
- });
- }
-
- function performValidation(valURL) {
- return new Promise(function(resolve, reject) {
- var xhr = new XMLHttpRequest();
- xhr.onload = function() {
- console.log(this.responseText);
- var data = JSON.parse(this.responseText);
- console.log(data);
- resolve(data);
- };
- xhr.onerror = reject;
- xhr.open('POST', 'validateApplePayMerchant.php', true);
- xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
- xhr.send('validationUrl='+valURL);
- });
- }
-
- session.onpaymentmethodselected = function(event) {
- console.log('starting onpaymentmethodselected');
- console.log(event);
- var newTotal = { type: 'final', label: 'Test Spices', amount: '15.00' };
- var newLineItems =[{type: 'final',label: 'Spice #202', amount: '15.00' }]
- session.completePaymentMethodSelection( newTotal, newLineItems);
- }
-
- session.onpaymentauthorized = function (event) {
- console.log('starting session.onpaymentauthorized');
- console.log(event);
- var promise = sendPaymentToken(event.payment.token);
- promise.then(function (success) {
- var status;
- if (success){
- status = ApplePaySession.STATUS_SUCCESS;
- console.log('Apple Pay Payment SUCCESS ');
- } else {
- status = ApplePaySession.STATUS_FAILURE;
- }
- console.log( "result of sendPaymentToken() function = " + success );
- session.completePayment(status);
- });
- }
-
- function sendPaymentToken(paymentToken) {
- return new Promise(function(resolve, reject) {
- console.log('starting function sendPaymentToken()');
- console.log(paymentToken);
-
- /* Send Payment token to Payment Gateway, here its defaulting to True just to mock that part */
-
- returnFromGateway = createTransaction(paymentToken.paymentData);
- console.log(returnFromGateway);
- console.log("defaulting to successful payment by the Token");
-
- if ( returnFromGateway == true )
- resolve(true);
- else
- reject;
- });
- }
-
-
- session.oncancel = function(event) {
- console.log('starting session.cancel');
- console.log(event);
- }
-
- session.begin();
+ let objJsonStr = JSON.stringify(dataObj);
+ console.log(objJsonStr);
+ let objJsonB64 = window.btoa(objJsonStr);
+ console.log(objJsonB64);
+ // Security: Get CSRF token from hidden input
+ var csrfToken = $("#csrfToken").val();
+ $.ajax({
+ url: "transactionCaller.php",
+ data: {
+ amount: "15.00",
+ dataDesc: "COMMON.APPLE.INAPP.PAYMENT",
+ dataValue: objJsonB64,
+ csrf_token: csrfToken, // Security: Include CSRF token
+ },
+ method: "POST",
+ timeout: 5000,
+ })
+ .done(function (data) {
+ console.log(data);
+ console.log("Success");
+ })
+ .fail(function () {
+ console.log("Error");
+ });
+
+ return true;
}
+function applePayButtonClicked() {
+ console.log("Apple Pay Initiated");
+
+ var request = {
+ countryCode: "US",
+ currencyCode: "USD",
+ supportedNetworks: ["visa", "masterCard", "amex", "discover"],
+ merchantCapabilities: ["supports3DS", "supportsCredit", "supportsDebit"], // Make sure NOT to include supportsEMV here
+ total: { label: "Test Spices", amount: "15.00" },
+ };
+
+ var session = new ApplePaySession(1, request);
+
+ // Merchant Validation
+ session.onvalidatemerchant = function (event) {
+ console.log(event);
+ var promise = performValidation(event.validationURL);
+ promise.then(function (merchantSession) {
+ session.completeMerchantValidation(merchantSession);
+ });
+ };
+
+ function performValidation(valURL) {
+ return new Promise(function (resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function () {
+ console.log(this.responseText);
+ var data = JSON.parse(this.responseText);
+ console.log(data);
+ resolve(data);
+ };
+ xhr.onerror = reject;
+ xhr.open("POST", "validateApplePayMerchant.php", true);
+ xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xhr.send("validationUrl=" + valURL);
+ });
+ }
+
+ session.onpaymentmethodselected = function (event) {
+ console.log("starting onpaymentmethodselected");
+ console.log(event);
+ var newTotal = { type: "final", label: "Test Spices", amount: "15.00" };
+ var newLineItems = [
+ { type: "final", label: "Spice #202", amount: "15.00" },
+ ];
+ session.completePaymentMethodSelection(newTotal, newLineItems);
+ };
+
+ session.onpaymentauthorized = function (event) {
+ console.log("starting session.onpaymentauthorized");
+ console.log(event);
+ var promise = sendPaymentToken(event.payment.token);
+ promise.then(function (success) {
+ var status;
+ if (success) {
+ status = ApplePaySession.STATUS_SUCCESS;
+ console.log("Apple Pay Payment SUCCESS ");
+ } else {
+ status = ApplePaySession.STATUS_FAILURE;
+ }
+ console.log("result of sendPaymentToken() function = " + success);
+ session.completePayment(status);
+ });
+ };
+
+ function sendPaymentToken(paymentToken) {
+ return new Promise(function (resolve, reject) {
+ console.log("starting function sendPaymentToken()");
+ console.log(paymentToken);
+
+ /* Send Payment token to Payment Gateway, here its defaulting to True just to mock that part */
+
+ returnFromGateway = createTransaction(paymentToken.paymentData);
+ console.log(returnFromGateway);
+ console.log("defaulting to successful payment by the Token");
+
+ if (returnFromGateway == true) resolve(true);
+ else reject;
+ });
+ }
+
+ session.oncancel = function (event) {
+ console.log("starting session.cancel");
+ console.log(event);
+ };
+
+ session.begin();
+}
diff --git a/certs/apple-pay-test-cert.pem b/certs/apple-pay-test-cert.pem
index 8a25880..19b3207 100644
--- a/certs/apple-pay-test-cert.pem
+++ b/certs/apple-pay-test-cert.pem
@@ -1,75 +1 @@
-Bag Attributes
- friendlyName: Merchant ID: merchant.authorize.net.test.dev15
- localKeyID: F9 19 81 DD 60 EE 9B 23 AA 9F 51 77 F6 FD DF 2D 68 0F 55 A1
-subject=/UID=merchant.authorize.net.test.dev15/CN=Merchant ID: merchant.authorize.net.test.dev15/OU=8WQ4DJVQC2/O=Authorize.Net LLC
-issuer=/C=US/O=Apple Inc./OU=Apple Worldwide Developer Relations/CN=Apple Worldwide Developer Relations Certification Authority
------BEGIN CERTIFICATE-----
-MIIGPTCCBSWgAwIBAgIIRtuR1IzrU2IwDQYJKoZIhvcNAQELBQAwgZYxCzAJBgNV
-BAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3Js
-ZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3
-aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
-HhcNMTYwOTIxMDU0ODE3WhcNMTgxMDIxMDU0ODE3WjCBnTExMC8GCgmSJomT8ixk
-AQEMIW1lcmNoYW50LmF1dGhvcml6ZS5uZXQudGVzdC5kZXYxNTE3MDUGA1UEAwwu
-TWVyY2hhbnQgSUQ6IG1lcmNoYW50LmF1dGhvcml6ZS5uZXQudGVzdC5kZXYxNTET
-MBEGA1UECwwKOFdRNERKVlFDMjEaMBgGA1UECgwRQXV0aG9yaXplLk5ldCBMTEMw
-ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDzIk/mcySzM8iyAgj8jIxJ
-KkWVthG2kjgCNpFYMe3hgBztpIsYiMVIvgBb2CV5NDhsp9NtptBaaAs0G6GRwjty
-khnPnytDbJrUocYkAOPkn37G5CFipj4z5Z6vii7jOoSTJeZA8io+zGyvrKMSqHby
-/VG/z34ngwD4qdgmx7zCKphAD6IuQGmqH+QWXmN3EVvF28xtFzTa7L9f8KVhQXXA
-FaiY1k7Yns+ahE0awVInsnC4mPuFNQ+TjTq/SdgqUC23l6rSMB2XRgmxCHDJCsfX
-L0uv2TCMeHG4KV7FXFn7hrreaftSOU+GaQ/RsaazLSF+7p3jVQxJnTQ/MnR7ceP5
-AgMBAAGjggKEMIICgDBHBggrBgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAGGK2h0dHA6
-Ly9vY3NwLmFwcGxlLmNvbS9vY3NwMDMtYXBwbGV3d2RyY2EyMDYwHQYDVR0OBBYE
-FPkZgd1g7psjqp9Rd/b93y1oD1WhMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU
-iCcXCam2GGCL7Ou69kdZxVJUo7cwggEsBgNVHSAEggEjMIIBHzCCARsGCSqGSIb3
-Y2QFATCCAQwwNgYIKwYBBQUHAgEWKmh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRp
-ZmljYXRlYXV0aG9yaXR5LzCB0QYIKwYBBQUHAgIwgcQMgcFSZWxpYW5jZSBvbiB0
-aGlzIENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBvdGhlciB0aGFuIEFwcGxlIGlz
-IHByb2hpYml0ZWQuIFJlZmVyIHRvIHRoZSBhcHBsaWNhYmxlIHN0YW5kYXJkIHRl
-cm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFu
-ZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMDAGA1UdHwQpMCcw
-JaAjoCGGH2h0dHA6Ly9jcmwuYXBwbGUuY29tL3d3ZHJjYS5jcmwwDgYDVR0PAQH/
-BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCME8GCSqGSIb3Y2QGIARCDEA3NUVC
-MjBEQzlCOTAzMjVGMDFFRUQxNEVCMUYxQTc2MUEwMEIxN0Q2RDUzRkMwNzFENUZC
-QzM2OThFQTBGMjA0MA8GCSqGSIb3Y2QGLgQCBQAwDQYJKoZIhvcNAQELBQADggEB
-AHHC6RRQeLUMc/W1ZQDXqOXAcZ87EqqhogieBkzaT8HeJsOe3pvgj1ShZbrk/kS6
-tT4PW/RzusEsbO7Mj0cD4aKXGB6oT6z9zwudxKwjDjuTk//qj30YPf15QXvo81Df
-Nh2BdS5KeaDoGTYp4gILWHlQoggDyXIFEqKhOgyql2Vb/F/8SttrxJjrbsiV/NFv
-1d7/Xpz0Vxo3e4CAOsJDGcwB9/F6GxN0nzMjxsZmLCgzsY99HRETsiScK7H3DI2n
-wHxSHmfajSvP1oTPaCE9DsRZdBQx3zys9/0CL9yQVsepNysyXqWq8FKihmVBIkYF
-RprycqKQoo/Yqv8P6aGe7X8=
------END CERTIFICATE-----
-Bag Attributes
- friendlyName: Authorize.Net Sandbox
- localKeyID: F9 19 81 DD 60 EE 9B 23 AA 9F 51 77 F6 FD DF 2D 68 0F 55 A1
-Key Attributes:
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,C0B79FBD8E42A430
-
-7RhxCZnlheJQ9Gqd+N4feoKrvfdkNQ2DHMo41N4u1qxcQdNJ1d2Bki/lRs5YZbj2
-f5i4F6ASiIm/bkCyJAagwcxncEI22/0fFLJ9KsP4ZKrBtXROE8kfGXzuQTAEVUG9
-yCx/qGVwTiDgQrhL1H8j0kCyXnQYAGqs+X07EifXCcH5feMzAK3bwR5tQ1YPb/kj
-d3WIlLHE7TLt1eHoeE5F+6um0TMxdClNX6gsHO8mwEpApBWfuvvS26aISiGndnjd
-9IbtChmOpnjkUCJVxgPvQWEtC5RIIXU/OwBmGYn7XkLCLYLVoqlOV1ZghBRhj/Dz
-GwuKNP9cvHeKWOel+v8X5GiNd/wABY6LJC+04HTsJ68o+cNooHUoVYBAjIV+Eljd
-QNpJMkY+6SxhfzZpTMhyQD97B6W0t99t7rETaq3TVquBXVpWamUHvjGBm/zSOYmZ
-P4UCub5CbgFUDSEVw5ZudludU6q9GPwir5RXbUrfKcPG0FUhOWazX8cmvaZwfEHu
-Ul68QgTkrP4IAhKylp0EDFhfgqjCqDp/8iKCHiUNg3j+oloOOCk6Dgd27RGSXPYl
-06MkhbT9jEM/a9/LK7u9e6PFQ/NxfNzZkF49cB22qgj9Kmd/vxEMouEi4H0Z/wwd
-0o3BQcILjOntYUif0YOV8PIKcU0P8vKu2Rk6rqaE9pcbVJumALgDgMSYCgca6axd
-R9o8BwXRd12E/+D0hvyH+/exj5xicLabW5VNcDIJrxUIKeWjOOfO/S0gPtvu0qLh
-NJpSm54li1bdouVB+AG+Abfy6U5XeS4pg1qjJNiYO1RHZuiVMibX15DOuM9JHGGQ
-/qRU3tDREY3Pam4gbjEZtG69LGmVSk+EM1kZWeAhVBztA1Pht7FYcn2IORaUmVzO
-IcMETqqi9pICpjj1/rX+on9YFgp7mUG7TQs8RpuncTZV/zoWF7oa/Jy9yFKeJ0rW
-TsqY9FzPVDNB8s3yZ+mG+lZpdVqsuWsI9NQfKjdbFHb5YZmrPfkpaLWfLXQ3tpUS
-F5B5apMFzqvHjmgYBip1T2Aig6ROTSzfMNjxf+ZOixN9XY3oXRmzoHNFrdq5vYZe
-9BI24HvuGxXXAd4RQbRbcPN7z9ijFJUiuw+TGt+tEPv8iZMU3THmCvCZKTYcO3xe
-mgK0su38W5/fYm/I5rapi3ti9XjHZS0S5LbU69x+NXPn2OWReD3pMAHGqGr7C0ri
-EUzpOZ+tCROjiHOLCR3uHSHlD0a831380I1p0i1QruKxH3mD6WKVgqOI9vzYbKGu
-9wc1IWvuZqpUYI+0D5eEFmN3+yXqcjKw7QJhBZVfnOfFvMpbhYMrLHeeE6qVtT5u
-w1+wJ0M8XaHDNjENbIM35Wy9oCL0smyYVwPKYZjJJlZ6a36Fv/HKbVNIHQok2sHd
-PC5Yc+7S7xGSHWQA+Gd66zcZ9usg495Tg00qkzFD62HI/932T62NbZqhkTHEgriZ
-u3+oI+WE33s3TZRtMziP3wJYBIMWymn0b4vu+deV3zVM19NFFiEt4/Dik2N6BpMr
-nb03Z5iBR9MbS/aYTBbhQpWLOCZcVdJDrUjNLWm0LfUNVa16b7GIZtCjb4JbM7TO
------END RSA PRIVATE KEY-----
+[INSERT YOUR APPLE PAY TEST CERT HERE]
\ No newline at end of file
diff --git a/chargeProfile.js b/chargeProfile.js
index cee3665..da1b875 100644
--- a/chargeProfile.js
+++ b/chargeProfile.js
@@ -1,17 +1,19 @@
-function showResult(msg)
-{
- try{
- responseObj=JSON.parse(msg);
- if(responseObj.transactionResponse.responseCode=='1'){
- message="Transaction Successful! Transaction ID: "+responseObj.transactionResponse.transId;
- }
- else{
- message="Transaction Unsuccessful.";//+responseObj.messages.message[0].text;
- if(responseObj.transactionResponse.errors!=null)//to do: take care of errors[1] array being parsed into single object
- {
- message+=responseObj.transactionResponse.errors.error.errorText;
- }
- /*else if(responseObj.transactionResponse.errors[0]!=null)
+function showResult(msg) {
+ try {
+ responseObj = JSON.parse(msg);
+ if (responseObj.transactionResponse.responseCode == "1") {
+ message =
+ "Transaction Successful! Transaction ID: " +
+ responseObj.transactionResponse.transId;
+ } else {
+ message = "Transaction Unsuccessful."; //+responseObj.messages.message[0].text;
+ if (
+ responseObj.transactionResponse.errors != null
+ ) //to do: take care of errors[1] array being parsed into single object
+ {
+ message += responseObj.transactionResponse.errors.error.errorText;
+ }
+ /*else if(responseObj.transactionResponse.errors[0]!=null)
{
for(i=0;i";
- message+=("Transaction ID: "+responseObj.transactionResponse.transId)
- }
- }
- }
- catch(error){
- console.log("Couldn't parse result string");
- message="Error.";
- }
-
- //alert(message);
-
- $('#acceptJSReceiptHeader').html("
ACCEPT.JS EXAMPLE
");
- $('#acceptJSReceiptBody').html(message);
- //jQuery.noConflict();
- $('#acceptJSReceiptModal').modal('show');
-}
+ if (responseObj.transactionResponse.transId != null) {
+ message += " ";
+ message += "Transaction ID: " + responseObj.transactionResponse.transId;
+ }
+ }
+ } catch (error) {
+ console.log("Couldn't parse result string");
+ message = "Error.";
+ }
-function createProfileTransaction() {
-
- $.ajax({
-
- url: "createTransactionWithProfile.php",
- // These profiles IDs would NOT BE HARDCODED of course but rather taken from the logged in user
- data: {amount: Math.floor((Math.random() * 100) + 1), customerProfileId: "1808251712", paymentProfileId: "1803116214"},
- method: 'POST',
- timeout: 5000
-
- }).done(function(data){
-
- console.log('Success');
-
- }).fail(function(){
-
- console.log('Error');
-
- }).always(function(textStatus){
-
- console.log(textStatus);
- showResult(textStatus);
-
- })
-
+ //alert(message);
+
+ $("#acceptJSReceiptHeader").html(
+ "
ACCEPT.JS EXAMPLE
",
+ );
+ $("#acceptJSReceiptBody").html(message);
+ //jQuery.noConflict();
+ $("#acceptJSReceiptModal").modal("show");
}
+function createProfileTransaction() {
+ // Security: Get CSRF token from hidden input
+ var csrfToken = $("#csrfToken").val();
+ $.ajax({
+ url: "createTransactionWithProfile.php",
+ // These profiles IDs would NOT BE HARDCODED of course but rather taken from the logged in user
+ data: {
+ amount: Math.floor(Math.random() * 100 + 1),
+ customerProfileId: "1808251712",
+ paymentProfileId: "1803116214",
+ csrf_token: csrfToken, // Security: Include CSRF token
+ },
+ method: "POST",
+ timeout: 5000,
+ })
+ .done(function (data) {
+ console.log("Success");
+ })
+ .fail(function () {
+ console.log("Error");
+ })
+ .always(function (textStatus) {
+ console.log(textStatus);
+ showResult(textStatus);
+ });
+}
diff --git a/createTransactionWithProfile.php b/createTransactionWithProfile.php
index b05511d..53ec084 100644
--- a/createTransactionWithProfile.php
+++ b/createTransactionWithProfile.php
@@ -1,4 +1,64 @@
'Unauthorized', 'message' => 'Authentication required']);
+ exit;
+}
+
+// SAMPLE ONLY:
+// Customer Profile IDs and Payment Profile IDs are issued by Authorize.Net
+// and are short numeric strings. In a production application you would
+// derive these from the authenticated user's profile in your own datastore,
+// not from POST. The pattern below shows the minimum input-validation shape
+// (filter_input with a digits-only regex + length cap).
+$customerProfileId = filter_input(INPUT_POST, 'customerProfileId',
+ FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^\d{1,13}$/']]);
+$paymentProfileId = filter_input(INPUT_POST, 'paymentProfileId',
+ FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^\d{1,13}$/']]);
+if (!$customerProfileId || !$paymentProfileId) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ echo json_encode(['error' => 'Bad Request', 'message' => 'Invalid customerProfileId / paymentProfileId.']);
+ exit;
+}
+
+// Security: Validate that authenticated user owns the profile
+// (Compares the validated customerProfileId against the session-bound CPID.
+// In production, derive the CPID from the authenticated user, never from POST.)
+$authenticatedCustomerId = $_SESSION['authenticated_cpid'];
+if ($authenticatedCustomerId !== $customerProfileId) {
+ http_response_code(403);
+ header('Content-Type: application/json');
+ echo json_encode(['error' => 'Forbidden', 'message' => 'You do not have permission to use this profile']);
+ exit;
+}
+
+// Security: Validate CSRF token (constant-time compare)
+if(!isset($_POST['csrf_token']) || !isset($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])){
+ http_response_code(403);
+ header('Content-Type: application/json');
+ echo json_encode(['error' => 'Forbidden', 'message' => 'Invalid CSRF token']);
+ exit;
+}
+
+// SAMPLE ONLY:
+// This sample validates the posted amount for demonstration purposes.
+// In a production application, do NOT rely on the client-submitted amount.
+// Instead, calculate the amount server-side from the authenticated user's
+// cart or order. The pattern below shows the minimum input-validation
+// shape (filter_input + range check + canonical decimal form).
+$amount = filter_input(INPUT_POST, 'amount', FILTER_VALIDATE_FLOAT);
+if ($amount === false || $amount === null || $amount <= 0 || $amount > 10000) {
+ http_response_code(400);
+ header('Content-Type: application/json');
+ echo json_encode(['error' => 'Bad Request', 'message' => 'Invalid transaction amount. Amount must be between $0.01 and $10,000.00']);
+ exit;
+}
+$amountCanonical = number_format($amount, 2, '.', '');
$transRequestXmlStr=<<
@@ -26,9 +86,13 @@
$transRequestXml->merchantAuthentication->addChild('name',$loginId);
$transRequestXml->merchantAuthentication->addChild('transactionKey',$transactionKey);
-$transRequestXml->transactionRequest->amount=$_POST['amount'];
-$transRequestXml->transactionRequest->profile->customerProfileId=$_POST['customerProfileId'];
-$transRequestXml->transactionRequest->profile->paymentProfile->paymentProfileId=$_POST['paymentProfileId'];
+// SAMPLE ONLY:
+// Use the validated locals from above (NOT raw $_POST) when assigning into
+// the outbound XML. This guarantees the value the gate validated is the
+// exact value sent on the wire.
+$transRequestXml->transactionRequest->amount = $amountCanonical;
+$transRequestXml->transactionRequest->profile->customerProfileId = $customerProfileId;
+$transRequestXml->transactionRequest->profile->paymentProfile->paymentProfileId = $paymentProfileId;
$url="https://apitest.authorize.net/xml/v1/request.api";
@@ -43,11 +107,10 @@
curl_setopt($ch, CURLOPT_POSTFIELDS, $transRequestXml->asXML());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
- // The following two curl SSL options are set to "false" for ease of development/debug purposes only.
- // Any code used in production should either remove these lines or set them to the appropriate
- // values to properly use secure connections for PCI-DSS compliance.
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //for production, set value to true or 1
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //for production, set value to 2
+ // SSL certificate verification enabled for secure connections and PCI-DSS compliance
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); //verify SSL certificate
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //verify certificate matches hostname
+ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); //enforce TLS 1.2 minimum
curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false );
$content = curl_exec($ch);
if (FALSE === $content)
diff --git a/getHostedPaymentForm.php b/getHostedPaymentForm.php
index f40cd7c..de43daa 100644
--- a/getHostedPaymentForm.php
+++ b/getHostedPaymentForm.php
@@ -79,11 +79,10 @@
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml->asXML());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
- // The following two curl SSL options are set to "false" for ease of development/debug purposes only.
- // Any code used in production should either remove these lines or set them to the appropriate
- // values to properly use secure connections for PCI-DSS compliance.
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //for production, set value to true or 1
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //for production, set value to 2
+ // SSL certificate verification enabled for secure connections and PCI-DSS compliance
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); //verify SSL certificate
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //verify certificate matches hostname
+ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); //enforce TLS 1.2 minimum
curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
//curl_setopt($ch, CURLOPT_PROXY, 'userproxy.visa.com:80');
$content = curl_exec($ch);
diff --git a/getProfiles.php b/getProfiles.php
index f461f2c..6406804 100755
--- a/getProfiles.php
+++ b/getProfiles.php
@@ -20,11 +20,10 @@
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml->asXML());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
- // The following two curl SSL options are set to "false" for ease of development/debug purposes only.
- // Any code used in production should either remove these lines or set them to the appropriate
- // values to properly use secure connections for PCI-DSS compliance.
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //for production, set value to true or 1
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //for production, set value to 2
+ // SSL certificate verification enabled for secure connections and PCI-DSS compliance
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); //verify SSL certificate
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //verify certificate matches hostname
+ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); //enforce TLS 1.2 minimum
curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false );
$content = curl_exec($ch);
$profileResponse = new SimpleXMLElement($content);
diff --git a/getToken.php b/getToken.php
index 2684874..42627d0 100755
--- a/getToken.php
+++ b/getToken.php
@@ -1,4 +1,14 @@
'Unauthorized', 'message' => 'Authentication required']);
+ exit;
+}
+
error_reporting(E_ERROR);
ini_set("log_errors", 1);
@@ -28,11 +38,10 @@
$xml->merchantAuthentication->addChild('name', $loginId);
$xml->merchantAuthentication->addChild('transactionKey', $transactionKey);
-if (isset($_COOKIE['cpid'])) {
- $cpid = $_COOKIE['cpid'];
-} else if (isset($_COOKIE['temp_cpid'])) {
- $cpid = $_COOKIE['temp_cpid'];
-}
+
+// Security: Read customer profile ID from server-side session (not user-controllable cookie)
+// This prevents authenticated attackers from accessing other users' profiles by modifying the cookie
+$cpid = $_SESSION['authenticated_cpid'];
$xml->customerProfileId = $cpid;
$xml->hostedProfileSettings->setting[0]->addChild('settingValue', curPageURL()."return.html");
@@ -50,11 +59,10 @@
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml->asXML());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 300);
- // The following two curl SSL options are set to "false" for ease of development/debug purposes only.
- // Any code used in production should either remove these lines or set them to the appropriate
- // values to properly use secure connections for PCI-DSS compliance.
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //for production, set value to true or 1
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); //for production, set value to 2
+ // SSL certificate verification enabled for secure connections and PCI-DSS compliance
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); //verify SSL certificate
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //verify certificate matches hostname
+ curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); //enforce TLS 1.2 minimum
curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false);
$content = curl_exec($ch);
$response = new SimpleXMLElement($content);
diff --git a/index.php b/index.php
index c961c0e..b93f6d9 100755
--- a/index.php
+++ b/index.php
@@ -1,5 +1,18 @@
messages->resultCode != "Ok") {
@@ -406,6 +419,7 @@ function onVisaCheckoutReady() {
+ '>
@@ -679,8 +693,8 @@ function onVisaCheckoutReady() {