cancel
Showing results for 
Search instead for 
Did you mean: 

PCI Compliance using XMLHTTPRequest

Hi Everyone,

 

I am trying to build Payment Solution in Microsoft Dynamics CRM online, which only accepts HTML and Javascript combination for custom code. Below is the code I put together using the API documentation. Bascially I'm using XMLHttpRequest to send Credit Card details to "https://apitest.authorize.net/xml/v1/request.api" for processing. We are not storing credit card details anywhere, just capturing the info and posting it. I am wondering If we are breaking any PCI Compliance rules here. Any suggestions are very much appreciated and thank you in advance:

<!DOCTYPE html>
<html>
<head>
    <style>
        table {
            font-family: arial, sans-serif;
            border-collapse: collapse;
            width: 100%;
        }

        td, th {
            border: 1px solid #dddddd;
            text-align: left;
            padding: 8px;
        }
    </style>
</head>
<body>
    <table id="PaymentTable">
        <tr>
            <td>Card Number</td>
            <td><input type="text" name="CardNumber" id="cardNumber" /></td>
        </tr>
        <tr>
            <td>Expiry Month</td>
            <td><input type="text" name="ExpMonth" id="ExpMonth" /></td>
        </tr>
        <tr>
            <td>Expiry Year</td>
            <td><input type="text" name="ExpYear" id="ExpYear" /></td>
        </tr>
        <tr>
            <td>CVV Code</td>
            <td><input type="text" name="CardCode" id="cardCode" /></td>
        </tr>
        <tr>
            <td>Billing Street Address</td>
            <td>
                <input type="text" name="BillingAddress" id="BillingAddress" />
            </td>
        </tr>
        <tr>
            <td>Zip Code</td>
            <td><input type="text" name="ZipCode" id="ZipCode" /> </td>
        </tr>
        <tr>
            <td>Amount</td>
            <td><input type="text" name="Amount" id="Amount" /> </td>
        </tr>
    </table> <br />
<button type="button" onclick="ProcessPayment()" id="Pay">Pay</button>
    <input type="text" name="WOId" id="WOId" style="visibility:hidden"/>
    <script src="JSBridge.js"></script>
    <script type="text/javascript">
        MobileCRM.UI.EntityForm.requestObject(
            function (entityForm) {
                var otherTab = entityForm.getDetailView("Other");
                var tbl = document.getElementById("PaymentTable");
                var num = otherTab.getItemByName("msdyn_totalamount").value;
                var n = num.toFixed(2);
                tbl.rows[6].cells[1].children[0].value = n;
                var entityId = entityForm.entity.id;
                document.getElementById("WOId").value = entityId;
                return true;
            },
            function (err) {
                alert('An error occurred: ' + err);
            },
            null
        );

        function ProcessPayment() {
                document.getElementById("Pay").disabled = true;
                var tbl = document.getElementById("PaymentTable");
                var CardNumber = tbl.rows[0].cells[1].children[0].value;
                var Month = tbl.rows[1].cells[1].children[0].value;
                var Year = tbl.rows[2].cells[1].children[0].value;
                var CardCode = tbl.rows[3].cells[1].children[0].value;
                var BillingAddress = tbl.rows[4].cells[1].children[0].value;
                var ZipCode = tbl.rows[5].cells[1].children[0].value;
                var Amount = tbl.rows[6].cells[1].children[0].value;

                var data = JSON.stringify({
                    "createTransactionRequest": {
                        "merchantAuthentication": {
                            "name": "3jB4s3mPac",
                            "transactionKey": "5Kw5583z7B9Dz63Q"
                        },
                        "refId": "",
                        "transactionRequest": {
                            "transactionType": "authCaptureTransaction",
                            "amount": Amount,
                            "payment": {
                                "creditCard": {
                                    "cardNumber": CardNumber,
                                    "expirationDate": Year + "-" + Month,
                                    "cardCode": CardCode
                                }
                            }
                        }
                    }
                });

                var xhr = new XMLHttpRequest();

                xhr.onreadystatechange = function () {
                    if (this.readyState == 4) {
                        if (this.responseText)
                        {
                            var response = JSON.parse(this.responseText);
                            var resultCode = String(response.messages.resultCode);
                            var transId = "";
                            var authCode = "";
                            var result = "";

                            var paymentRecord = new MobileCRM.DynamicEntity.createNew("msdyn_payment");

                            if (resultCode == "Ok")
                            {
                                result = response.transactionResponse.messages[0].description;
                                transId = response.transactionResponse.transId;
                                authCode = response.transactionResponse.authCode;
                                paymentRecord.properties["ecl_authcode"] = authCode;
                                paymentRecord.properties["msdyn_name"] = transId;
                                paymentRecord.properties["msdyn_amount"] = tbl.rows[6].cells[1].children[0].value;
                            }
                            else
                                result = response.transactionResponse.errors[0].errorText;
                            
                            paymentRecord.properties["ecl_description"] = result;
                            paymentRecord.properties["msdyn_paymenttype"] = 690970002;
                            paymentRecord.properties["msdyn_workorder"] = new MobileCRM.Reference("msdyn_workorder", document.getElementById("WOId").value);
                            
                            alert(result);

                            // Creating the new violation
                            paymentRecord.save(
                                function (error) {
                                    if (error)
                                        alert("An error occurred: " + error);
                                }
                            );		

                            tbl.rows[0].cells[1].children[0].value = "";
                            tbl.rows[1].cells[1].children[0].value = "";
                            tbl.rows[2].cells[1].children[0].value = "";
                            tbl.rows[3].cells[1].children[0].value = "";
                            tbl.rows[4].cells[1].children[0].value = "";
                            tbl.rows[5].cells[1].children[0].value = "";
                            document.getElementById("Pay").disabled = false;
                        }
                    }
                };

                xhr.open("POST", "https://apitest.authorize.net/xml/v1/request.api");
                xhr.setRequestHeader("content-type", "application/json");
                xhr.send(data);
        }
    </script>

</body>
</html>
jogendar
Member
2 REPLIES 2
@jogendar

Without looking at your code in much depth, there are 2 potential compliance scopes you will fall under. Those are SAQ A-EP and SAQ D. If you want to know the requirements, go to the PCI document library and pull those SAQs. You are most likely SAQ D. The difference is SAQ A-EP you are passing tokenized credit card data across your server, and SAQ D applies if you pass raw credit card data across your server.

The fact that you do not store credit card data on your servers doesn’t reduce your compliance burden once you hit level SAQ D. For ecommerce that is as restrictive as it gets. If you get audited and fail, you will probably get a lesser fine than if you did store cc data.

PCI isn’t about what you don’t do as much as it is about what you do do. There is a 67 page list of requirements for ecommerce. Depending on your scope, which is determined by how you integrate, you may have to comply with just a handful, or a whole lot, or in your case every single one of them (assuming SAQ D, which you almost certainly fall under).

Depending on the volume of transactions you do, you will either be subject to audit or you will self attest to compliance, with the ongoing possibility that you get audited at any time. Will be triggered by something, like a complaint against you. When you get fined, the figures I have heard are around $200 to $400 per transaction record in your database. So whatever table is tied to your customers order that generates the charge, multiply that row count by those figures and you’ve got a rough range of how bankrupt you’re going to be. I’ve seen clients with 50,000+ records. Disclaimer that my figures are just what I have came across online, in regards to fines.
Renaissance
All Star

For one thing, in this case, your Authorize.net login information would be exposed. Regarding the PCI compliance, the way that Authorize.net's Accept.js minimizes your PCI compliance is by posting a payment nonce in the place of payment data for the createTransactionRequest API call. Which would not happen using the Javascript / HTML you have posted. IMO, a better solution for PCI compliance would be  to use an Iframe or Dialog box Accept Hosted Payment form or use the Accept.JS. You could do an Ajax request to your own payment processing script in order to receive the payment token for insertion into your form. If you don't already have another server, you could get a cheap VPS for around $5 a month strictly for hosting your own payment processing application.

Powered by NexWebSites.com -
Certified Authorize.net developers
NexusSoftware
Trusted Contributor