aviabird / gringotts

A complete payment library for Elixir and Phoenix Framework
https://hexdocs.pm/gringotts/Gringotts.html
MIT License
481 stars 53 forks source link

Authorize.Net optional fields are not optional #181

Open ghost opened 5 years ago

ghost commented 5 years ago

Documentation labels the opts as optional but if you try to submit without them you get an error.

iex> amount = Money.new(20, :USD)
iex> card = %CreditCard{number: "5424000000000015", year: 2099, month: 12, verification_code: "999"}
iex> result = Gringotts.purchase(Gringotts.Gateways.AuthorizeNet, amount, card, [])

{:error,
 %{
   __struct__: Gringotts.Response,
   authorization: nil,
   avs_result: nil,
   cvc_result: nil,
   error_code: nil,
   fraud_review: nil,
   message: nil,
   params: nil,
   raw: %{
     "ErrorResponse" => %{
       "messages" => %{
         "message" => %{
           "code" => "E00003",
           "text" => "The 'AnetApi/xml/v1/schema/AnetApiSchema.xsd:itemId' element is invalid - The value '' is invalid according to its datatype 'String' - The actual length is less than the MinLength value." 
         },
         "resultCode" => "Error"
       }
     }
   },
   status_code: nil,
   success: false
 }}

When I look at the payload in Gateways.AuthorizeNet.commit/3 it looks like the opts are included as blanks anyway. I believe this is throwing a bug on Authorize.Net's end where when they see the "lineItems" node they expect a "lineItem" with a "itemId" with length greater than 0.

pry(2)> XmlToMap.naive_map(payload)
%{
  "createTransactionRequest" => %{
    "merchantAuthentication" => %{
      "name" => "6X3g9dVnZ",
      "transactionKey" => "8eP2Jc7SA7a3h3Gy"
    },
    "refId" => %{},
    "transactionRequest" => %{
      "amount" => "5",
      "billTo" => %{
        "address" => %{},
        "city" => %{},
        "company" => %{},
        "country" => %{},
        "firstName" => %{},
        "lastName" => %{},
        "state" => %{},
        "zip" => %{}
      },
      "customer" => %{"id" => %{}},
      "customerIP" => %{},
      "duty" => %{"amount" => "0", "description" => %{}, "name" => %{}},
      "lineItems" => %{
        "lineItem" => %{
          "description" => %{},
          "itemId" => %{},
          "name" => %{},
          "quantity" => %{},
          "unitPrice" => %{}
        }
      },
      "order" => %{"description" => %{}, "invoiceNumber" => %{}},
      "payment" => %{
        "creditCard" => %{
          "cardCode" => "999",
          "cardNumber" => "5424000000000015",
          "expirationDate" => "2020-12"
        }
      },
      "poNumber" => %{},
      "shipTo" => %{
        "address" => %{},
        "city" => %{},
        "company" => %{},
        "country" => %{},
        "firstName" => %{},
        "lastName" => %{},
        "state" => %{},
        "zip" => %{}
      },
      "shipping" => %{"amount" => "0", "description" => %{}, "name" => %{}},
      "tax" => %{"amount" => "0", "description" => %{}, "name" => %{}},
      "transactionType" => "authOnlyTransaction"
    }
  }
}

I tested this structure:

<createTransactionRequest xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
  <merchantAuthentication>
    <name>78BZ5Xprry</name>
    <transactionKey>8s2F95Q7brhHd7Tn</transactionKey>
  </merchantAuthentication>
  <transactionRequest>
    <transactionType>authCaptureTransaction</transactionType>
    <amount>5</amount>
    <payment>
      <creditCard>
        <cardNumber>5424000000000015</cardNumber>
        <expirationDate>2020-12</expirationDate>
        <cardCode>999</cardCode>
      </creditCard>
    </payment>
    <order>
     <invoiceNumber>INV-12345</invoiceNumber>
     <description>Product Description</description>
    </order>
  </transactionRequest>
</createTransactionRequest>

on Authorize.Net's API reference and I got a successful response, so it doesn't seem necessary to include all the blank options in the request if there happen to be no options included. May I suggest only submitting opts that the user submits in the call to Gringotts.purchase as a solution to this problem?

arjun289 commented 5 years ago

@cmititiuc Looking into this.