mitodl / micromasters

Portal for learners and course teams to access MITx Micromasters® programs
https://mm.mit.edu
BSD 3-Clause "New" or "Revised" License
30 stars 17 forks source link

pay for a course on edX #844

Closed pdpinch closed 8 years ago

pdpinch commented 8 years ago

As a user I'd like to pay for my course on edX.

On clicking enroll, we'll ask user for credit card information On clicking save, we'll charge user's credit card using MIT's CyberSource account

noisecapella commented 8 years ago

There's Visa Checkout which provides a UI similar to Stripe Checkout (I think), however it supports very few countries, none in Europe

noisecapella commented 8 years ago

I found a list of fields we would need to have in our UI. From http://apps.cybersource.com/library/documentation/dev_guides/CC_Svcs_SO_API/Credit_Cards_SO_API.pdf page 36 (ignore unicode errors and numbers after each field, which are footnotes):

 billTo_city8  billTo_country8  billTo_email8  billTo_firstName8  billTo_lastName8  billTo_postalCode3,8  billTo_state3,8  billTo_street18  card_accountNumber  card_cardType6  card_expirationMonth8  card_expirationYear8  ccAuthService_run  merchantID  merchantReferenceCode  purchaseTotalscurrency  purchaseTotals grandTotalAmount4

noisecapella commented 8 years ago

It looks like card_cvNumber is the field which takes the CVN (aka CVV)

noisecapella commented 8 years ago

Detecting the credit card from the number: https://www.cybersource.com/developers/getting_started/test_and_manage/best_practices/card_type_id/

noisecapella commented 8 years ago

On tokenization:

CyberSource payment tokenization and payment network tokenization are different features:

The CyberSource token (the profile ID) is created by CyberSource and can be used only with CyberSource payment services.

The payment network token is created by a token service provider and can be used throughout the financial network.

From http://apps.cybersource.com/library/documentation/dev_guides/Payment_Tokenization/SO_API/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=ch_intro.4.1.html#1164077

noisecapella commented 8 years ago

Here's sample XML from an outbound request I got from their PHP client:

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:schemas-cybersource-com:transaction-data-1.120" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
  <SOAP-ENV:Header>
    <ns2:Security SOAP-ENV:mustUnderstand="1">
      <ns2:UsernameToken>
        <ns2:Username>mit_odl_ccx</ns2:Username>
        <ns2:Password>your_transaction_key</ns2:Password>
      </ns2:UsernameToken>
    </ns2:Security>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns1:requestMessage>
      <ns1:merchantID>mit_odl_ccx</ns1:merchantID>
      <ns1:merchantReferenceCode>your_merchant_reference_code</ns1:merchantReferenceCode>
      <ns1:clientLibrary>CyberSource PHP 1.0.0</ns1:clientLibrary>
      <ns1:clientLibraryVersion>5.6.11-1ubuntu3.4</ns1:clientLibraryVersion>
      <ns1:clientEnvironment>Linux george-laptop 4.2.0-41-generic #48-Ubuntu SMP Fri Jun 24 11:28:43 UTC 2016 x86_64</ns1:clientEnvironment>
      <ns1:billTo>
        <ns1:firstName>John</ns1:firstName>
        <ns1:lastName>Doe</ns1:lastName>
        <ns1:street1>1295 Charleston Road</ns1:street1>
        <ns1:city>Mountain View</ns1:city>
        <ns1:state>CA</ns1:state>
        <ns1:postalCode>94043</ns1:postalCode>
        <ns1:country>US</ns1:country>
        <ns1:email>null@cybersource.com</ns1:email>
        <ns1:ipAddress>10.7.111.111</ns1:ipAddress>
      </ns1:billTo>
      <ns1:purchaseTotals>
        <ns1:currency>USD</ns1:currency>
        <ns1:grandTotalAmount>90.01</ns1:grandTotalAmount>
      </ns1:purchaseTotals>
      <ns1:card>
        <ns1:accountNumber>4111111111111111</ns1:accountNumber>
        <ns1:expirationMonth>12</ns1:expirationMonth>
        <ns1:expirationYear>2020</ns1:expirationYear>
      </ns1:card>
      <ns1:ccAuthService run="true"/>
      <ns1:ccCaptureService run="true"/>
    </ns1:requestMessage>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
noisecapella commented 8 years ago

On the Javascript client we would need to send this XML to create a profile for the customer. (Not sure if existing profiles are updated automatically.) After issuing this command we would get a subscription id in the response, and we send that to our server. (More next comment on that)

From here:

<?xml version="1.0"?>
<requestMessage xmlns="urn:schemas-cybersource-com:transaction-data-1.92">
  <merchantID>infodev</merchantID>
  <merchantReferenceCode>14344</merchantReferenceCode>
  <billTo>
    <firstName>John</firstName>
    <lastName>Doe</lastName>
    <street1>1295 Charleston Road</street1>
    <city>Mountain View</city>
    <state>CA</state>
    <postalCode>94043</postalCode>
    <country>US</country>
    <email>john.doe@example.com</email>
  </billTo>
  <purchaseTotals>
    <currency>USD</currency>
  </purchaseTotals>
  <card>
    <accountNumber>4111111111111111</accountNumber>
    <expirationMonth>12</expirationMonth>
    <expirationYear>2015</expirationYear>
    <cardType>001</cardType>
  </card>
  <recurringSubscriptionInfo>
    <frequency>on-demand</frequency>
  </recurringSubscriptionInfo>
  <paySubscriptionCreateService run="true"/>
</requestMessage>

Response:

<?xml version="1.0"?>
<c:replyMessage xmlns:c="urn:schemas-cybersource-com:transaction-data-1.92">
  <c:ccAuthReply>
    <c:amount>0.00</c:amount>
    <c:authorizationCode>888888</c:authorizationCode>
    <c:authorizationDateTime>2013-09-13T10:14:06Z</c:authorizationDateTime>
    <c:avsCode>X</c:avsCode>
    <c:reasonCode>100</c:reasonCode>
    <c:reconciliationID>40368790XLILGOLX</c:reconciliationID>
  </c:ccAuthReply>
  <c:merchantReferenceCode>1111</c:merchantReferenceCode>
  <c:requestID>3790672461500176056470</c:requestID>
  <c:decision>ACCEPT</c:decision>
  <c:reasonCode>100</c:reasonCode>
  <c:paySubscriptionCreateReply>
    <c:reasonCode>100</c:reasonCode>
    <c:subscriptionID>0000562489861111</c:subscriptionID>
  </c:paySubscriptionCreateReply>
  <c:purchaseTotals>
    <c:currency>USD</c:currency>
  </c:purchaseTotals>
</c:replyMessage>
noisecapella commented 8 years ago

On the server side we receive the subscription id and the merchant reference code from the client, then make a sale. The XML looks similar to what I posted in the comment above but most fields would be omitted. The docs (page 106) say that only four pieces of data are required for the sale: Merchant ID, Merchant reference code, Amount of the payment or credit, and the Subscription ID.

noisecapella commented 8 years ago

One other thing to note, all requests go to a single endpoint, https://ics2wstest.ic3.com:443/commerce/1.x/transactionProcessor for the test account. The intent of the message is determined using service names. In the request above that's <paySubscriptionCreateService run="true"/>.

For the sale request above you see two service names: <ccAuthService run="true"/> and <ns1:ccCaptureService run="true"/>. This tells CyberSource to both request authorization and capture of funds at the same time. From this, page 66:

A sale is a bundled authorization and capture. You can use a sale instead of a separate authorization and capture if there is no delay between taking a customer’s order and shipping the goods. A sale is typically used for electronic goods and for services that you can turn on immediately.

This seems like what we want

noisecapella commented 8 years ago

What is the difference between an authorization and capture?

For a transaction to be completed successfully, you need to perform an authorization and a capture on the transaction. An authorization ensures that your customer's credit account is open, in good standing, and has sufficient funds to complete the submitted transaction. If the card passes these checks, the issuing bank will place a hold on the funds for the amount of the authorization.

noisecapella commented 8 years ago

In the docs there are references to sending XML or sending name value pairs. These are two different formats for sending the same commands. From Getting Started, page 16:

XML element names and the name-value pair field names are related in the following ways:

  • Each name-value pair field name matches the corresponding XML element name.
  • The XML schema shows hierarchy with an underscore ( _ ) separating the name of the parent element from the name of the child element.

The docs generally show only name value pairs in their inline examples. They look like this for example:

billTo_firstName=John billTo_lastName=Doe billTo_street1=1295 Charleston Road billTo_city=Mountain View billTo_state=CA billTo_postalCode=94043 billTo_country=US billTo_email=null@cybersource.com purchaseTotals_currency=USD card_accountNumber=4111111111111111 card_expirationMonth=12 card_expirationYear=2018 card_cardType=001 merchantID=demoID merchantReferenceCode=1111 recurringSubscriptionInfo_frequency=on-demand paySubscriptionCreateService_run=true

Note that this data is still sent as SOAP data, as a piece of text inside a SOAP envelope (the nvpRequest element here):

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:schemas-cybersource-com:transaction-data-1.120">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:nvpRequest>

      </urn:nvpRequest>
   </soapenv:Body>
</soapenv:Envelope>

And the reply data is also in name value pair form. Therefore I recommend we use the XML format instead. The instructions for translating between the two are in Getting Started, page 16.

noisecapella commented 8 years ago

Regarding authorization, this is sent in the SOAP header. From the sale request XML above:

<SOAP-ENV:Header>
    <ns2:Security SOAP-ENV:mustUnderstand="1">
      <ns2:UsernameToken>
        <ns2:Username>mit_odl_ccx</ns2:Username>
        <ns2:Password>your_transaction_key</ns2:Password>
      </ns2:UsernameToken>
    </ns2:Security>
  </SOAP-ENV:Header>

This wasn't obvious when I was browsing the WSDL in soapui. There's an example SOAP message with this information, page 15:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/ wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
      <wsse:UsernameToken>
        <wsse:Username>
yourMerchantID
</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss- username-token-profile-1.0#PasswordText">
yourPassword
</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <requestMessage xmlns="urn:schemas-cybersource-com:transaction-data- N.NN ">
      <merchantID>
yourMerchantID
</merchantID>
      <merchantReferenceCode>MRC-123</merchantReferenceCode>
      <billTo>
        <firstName>John</firstName>
        <lastName>Doe</lastName>
        <street1>1295 Charleston Road</street1>
        <city>Mountain View</city>
        <state>CA</state>
        <postalCode>94043</postalCode>
        <country>US</country>
        <email>null@cybersource.com</email>
      </billTo>
      <item id="0">
        <unitPrice>5.00</unitPrice>
        <quantity>1</quantity>
      </item>
      <item id="1">
        <unitPrice>10.00</unitPrice>
        <quantity>2</quantity>
      </item>
      <purchaseTotals>
        <currency>USD</currency>
      </purchaseTotals>
      <card>
        <accountNumber>4111111111111111</accountNumber>
        <expirationMonth>11</expirationMonth>
        <expirationYear>2020</expirationYear>
      </card>
      <ccAuthService run="true"/>
    </requestMessage>
  </soapenv:Body>
</soapenv:Envelope>
noisecapella commented 8 years ago

Information about the 'Merchant Reference Code':

The merchant reference code is an order tracking number that you generate and send in your requests so that you can track orders as they move through the different phases of processing with CyberSource.

From here, page 12

noisecapella commented 8 years ago

Bad news... it looks like the endpoint is not CORS compliant, so I don't think we can work with it from our browser. I'm looking to see if there's any other information on whether this is supported

pdpinch commented 8 years ago

Seriously?

I wonder what edX does... Do you think they store and forward the credit card numbers?

Bad news... it looks like the endpoint is not CORS compliant, so I don't think we can work with it from our browser. I'm looking to see if there's any other information on whether this is supported

noisecapella commented 8 years ago

It looks like they use Secure Acceptance which is what we looked at earlier, but it had the problem of the user being able to set the total amount for the transaction. I'm looking at whether they have the same problem

noisecapella commented 8 years ago

Just to document what we said in hipchat, we can create an HMAC signature which will secure all the fields we care about from being edited. For documentation on that see this, page 27

noisecapella commented 8 years ago

From here, along with the signature we also need to send signed_field_names which is a comma delimited list of fields we are signing, which must be in the order that they are signed. This code from edX might be helpful

noisecapella commented 8 years ago

@pdpinch what's the scope of this issue? Since we're not using the Simple Order API should I close it and open new issues which are more specific?

pdpinch commented 8 years ago

We'd like to keep this open as an epic.

If you find more specific issues helpful and not too much trouble (I do), you can open them and add a checklist and/or create a zenhub epic.

I hope that's not too complicated.