package divconq.interchange.authorize; import java.io.DataOutputStream; import java.math.BigDecimal; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import divconq.lang.op.FuncCallback; import divconq.lang.op.FuncResult; import divconq.lang.op.OperationContext; import divconq.lang.op.OperationResult; import divconq.struct.ListStruct; import divconq.struct.RecordStruct; import divconq.struct.Struct; import divconq.util.StringUtil; import divconq.xml.XAttribute; import divconq.xml.XElement; import divconq.xml.XmlReader; public class AuthUtil { static public final String AUTH_TEST_ENDPOINT = "https://apitest.authorize.net/xml/v1/request.api"; static public final String AUTH_LIVE_ENDPOINT = "https://api.authorize.net/xml/v1/request.api"; static public void authXCard(String authid, String authkey, String refid, boolean live, boolean test, RecordStruct order, FuncCallback<RecordStruct> callback) { OperationResult res = order.validate("dcmOrderInfo"); if (res.hasErrors()) { callback.complete(); return; } if (order.isFieldEmpty("PaymentInfo")) { callback.error("Missing payment details."); callback.complete(); return; } RecordStruct paymentinfo = (RecordStruct) order.removeField("PaymentInfo").getValue(); RecordStruct custinfo = order.getFieldAsRecord("CustomerInfo"); // required RecordStruct billinginfo = order.getFieldAsRecord("BillingInfo"); // not required RecordStruct shipinfo = order.getFieldAsRecord("ShippingInfo"); // not required RecordStruct calcinfo = order.getFieldAsRecord("CalcInfo"); // not required in schema if (paymentinfo.isFieldEmpty("CardNumber") || paymentinfo.isFieldEmpty("Expiration") || paymentinfo.isFieldEmpty("Code")) { callback.error("Missing payment details."); callback.complete(); return; } if (billinginfo == null) { callback.error("Missing billing details."); callback.complete(); return; } if (calcinfo == null) { callback.error("Missing billing computations."); callback.complete(); return; } XElement root = new XElement("createTransactionRequest", new XAttribute("xmlns", "AnetApi/xml/v1/schema/AnetApiSchema.xsd")); root.add( new XElement("merchantAuthentication", new XElement("name", authid), new XElement("transactionKey", authkey) ) ); root.add( new XElement("refId", refid.replace("_", "")) ); BigDecimal tax = calcinfo.getFieldAsDecimal("TaxTotal"); BigDecimal ship = calcinfo.getFieldAsDecimal("ShipTotal"); BigDecimal total = calcinfo.getFieldAsDecimal("GrandTotal"); XElement txreq = new XElement("transactionRequest", new XElement("transactionType", "authCaptureTransaction"), // or authOnlyTransaction new XElement("amount", total.toPlainString()), new XElement("payment", new XElement("creditCard", new XElement("cardNumber", paymentinfo.getFieldAsString("CardNumber")), new XElement("expirationDate", paymentinfo.getFieldAsString("Expiration")), new XElement("cardCode", paymentinfo.getFieldAsString("Code")) ) ) ); ListStruct items = order.getFieldAsList("Items"); if ((items != null) && (items.getSize() > 0)) { XElement ilist = new XElement("lineItems"); for (Struct i : items.getItems()) { RecordStruct itm = (RecordStruct) i; String price = itm.isFieldEmpty("SalePrice") ? itm.getFieldAsString("Price") : itm.getFieldAsString("SalePrice"); if (StringUtil.isEmpty(price)) price = "0"; String title = itm.getFieldAsString("Title"); if (StringUtil.isEmpty(title)) title = "[unkown]"; if (title.length() > 31) title = title.substring(0, 31); String desc = itm.getFieldAsString("Description"); if (StringUtil.isEmpty(desc)) desc = "[not availale]"; if (desc.length() > 255) desc = desc.substring(0, 255); XElement iline = new XElement("lineItem"); if (itm.hasField("Sku")) iline.add(new XElement("itemId").withText(StringUtil.stripAllNonAscci(itm.getFieldAsString("Sku")))); iline.add(new XElement("name").withText(StringUtil.stripAllNonAscci(title))); //iline.add(new XElement("description").withText(desc)); iline.add(new XElement("quantity").withText(itm.getFieldAsString("Quantity"))); iline.add(new XElement("unitPrice").withText(price)); ilist.add(iline); } txreq.add(ilist); } txreq.add( new XElement("tax", new XElement("amount", tax.toPlainString()), new XElement("name", billinginfo.getFieldAsString("State")) ) ); txreq.add( new XElement("shipping", new XElement("amount", ship.toPlainString()), new XElement("name", billinginfo.getFieldAsString("State")) ) ); if (!custinfo.isFieldEmpty("CustomerId")) txreq.add( new XElement("customer", new XElement("id", custinfo.getFieldAsString("CustomerId").replace("_", "")), new XElement("email", custinfo.getFieldAsString("Email")) ) ); else txreq.add( new XElement("customer", new XElement("email", custinfo.getFieldAsString("Email")) ) ); txreq.add( new XElement("billTo", new XElement("firstName", billinginfo.getFieldAsString("FirstName")), new XElement("lastName", billinginfo.getFieldAsString("LastName")), new XElement("address", billinginfo.getFieldAsString("Address")), new XElement("city", billinginfo.getFieldAsString("City")), new XElement("state", billinginfo.getFieldAsString("State")), new XElement("zip", billinginfo.getFieldAsString("Zip")), new XElement("country", "USA"), // TODO add international support new XElement("phoneNumber", custinfo.getFieldAsString("Phone")) ) ); if (shipinfo != null) txreq.add( new XElement("shipTo", new XElement("firstName", shipinfo.getFieldAsString("FirstName")), new XElement("lastName", shipinfo.getFieldAsString("LastName")), new XElement("address", shipinfo.getFieldAsString("Address")), new XElement("city", shipinfo.getFieldAsString("City")), new XElement("state", shipinfo.getFieldAsString("State")), new XElement("zip", shipinfo.getFieldAsString("Zip")), new XElement("country", "USA") // TODO add international support ) ); String origin = OperationContext.get().getOrigin(); // track web customers if (StringUtil.isNotEmpty(origin) && origin.startsWith("http:")) txreq.add(new XElement("customerIP", origin.substring(5))); /* TODO possible enhancements XElement settings = new XElement("transactionSettings"); if (test) settings.add( new XElement("setting", new XElement("settingName", "testRequest"), new XElement("settingValue", "true") ) ); settings.add( new XElement("setting", new XElement("settingName", "emailCustomer"), new XElement("settingValue", "false") ) ); txreq.add(settings); */ root.add(txreq); // Auth documentation: // http://developer.authorize.net/api/reference/ try { OperationContext.get().touch(); URL url = new URL(live ? AUTH_LIVE_ENDPOINT : AUTH_TEST_ENDPOINT); HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); con.setRequestMethod("POST"); con.setRequestProperty("User-Agent", "DivConq/1.0 (Language=Java/8)"); con.setRequestProperty("Content-Type", "text/xml"); String body = root.toString(); // Send post request con.setDoOutput(true); DataOutputStream wr = new DataOutputStream(con.getOutputStream()); wr.writeBytes(body); wr.flush(); wr.close(); int responseCode = con.getResponseCode(); if (responseCode == 200) { // parse and close response stream FuncResult<XElement> xres = XmlReader.parse(con.getInputStream(), false); XElement resp = xres.getResult(); XElement tr = resp.find("transactionResponse"); if (tr == null) { callback.error("Error processing payment: Gateway sent an incomplete response."); callback.setResult( new RecordStruct() .withField("Message", resp) ); } else { XElement trc = tr.find("responseCode"); XElement trid = tr.find("transId"); if ((trc == null) || (trid == null)) { callback.error("Error processing payment: Gateway sent an incomplete transaction response."); callback.setResult( new RecordStruct() .withField("Message", resp) ); } else { String rcodeout = trc.getText(); String txidout = trid.getText(); if (!"1".equals(rcodeout)) callback.error("Payment was rejected by gateway"); callback.setResult( new RecordStruct() .withField("Code", rcodeout) .withField("TxId", txidout) .withField("Message", resp) ); } } } else callback.error("Error processing payment: Unable to connect to payment gateway."); } catch (Exception x) { callback.error("Error processing payment: Unable to connect to payment gateway."); } callback.complete(); } }