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>
07-18-2019 08:17 AM
07-18-2019 10:06 AM - edited 07-18-2019 10:07 AM
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.
07-22-2019 04:06 PM