The origin of the article is https://idnforums.intuit.com/messageview.aspx?catid=3&threadid=7743&FTVAR_STKEYWORDFRM=&STARTPAGE=1&FTVAR_FORUMVIEWTMP=Linear

I am just adding my own notes (in green) here.

——————————–

This document serves as a brief description of how (and why) quickbooks merchant services (QBMS) works with PHP to allow merchants to process credit card transactions.

Intuit has done a pretty decent job of documenting this using java serverlets and ASP.NET, but documentation for doing this using PHP is hard to come by.

This document is intended to serve as internal documentation of the process as well as a guide for future developers to develop robust applications that work well with PHP and Quickbooks.

 

1. The first thing that an application developer needs to do is apply for a merchant services developer account. This is done via the quicken website at the following URL.

https:/merchantaccount.ptc.quickbooks.com/j/mas/signup?nonQBmerchant=true

This URL is a link to Intuit’s production software. The “nonQBmerchant=true” portion of the URL lets the software know that you are a developer. During the signup process it will seem like you are signing up for an actual account.

• Follow the prompts to obtain your test account. These are self explanatory. However, you’ll need to know a few things that we’ll cover here:

When prompted to supply a valid email address, make sure you supply a valid address that has not been used before in any of the QB Online Edition or QBMS test or production environments. (Ignore any fee information for the test environment: there are no fees in the test environment for test transactions.) That email address will be your login to the test account when you need to login for connection tickets or session tickets.

After you finish applying for the QBMS account, you will see a page that says “Application Submitted.” At this point you are done and you have a ready to use PTC QBMS account! Ignore the “Important Next Steps.”

You will receive an email to the effect that the account is pending. Ignore this email: the account is activated at this point and ready for use.
This step is easy with no confusion.

2. Now you need to sign up for a developer account. This account permits you to create and issue the certificates that are necessary for performing transactions with the Intuit services. This account can also be created via the quicken website at the following URL:

http://appreg.intuit.com

Once you have created an account with this system, you can create your project. It is important that when you do this you set the variables to the proper values.

The AppID is the unique key for this application. Is is generated when you create you application on the website and is used to generate a “connection ticket” later on.

In the above example, the AppLogin is gateway.lawrencefreenet.org. This is the NAME of the application that you are developing.

The remainder of this is information that you should have. One thing that you need to be aware of:

When creating the application, Quickbooks will ask for an “https:\\” callback url for various functions. It is VERY important that these URLs have a VALID SSL certificate issued by a recognized Certificat Authority (CA). We use RapidSSL here and have never had any trouble with their service or certificates.

It is also VERY important that this URL actually exist. It is going to be used when you get your “connection ticket”

In our case this url is:

https://www.lawrencefreenet.org/intuit.php

3. Issuing Certificate
Now that you have created the Gateway Application account, you are going to need to create a certificate using Quicken’s certificate tools.

What is this certificate? It is a list of information that allows Quicken to identify your unique application and authenticate it to Quicken’s system.

Basically, when you want to do a transaction for a Merchant, you need two things:

1. A valid certificate issued by Quicken.
2. A valid connection ticket from the merchant whose account you are connecting to.

We will get into the second item in just a minute, but first we need to talk about how to issue a valid certificate.
On our system we are using OpenSSL. Looking at Intuit’s system, it looks as though they are also using this software. Since we couldn’t find instructions for how to do this using OpenSSL and Apache2 on Intuit’s website, the commands are as follows.  (Not sure if this matters, but you probably already have a KEY file: the one that you used to generate a CSR that you then sent to a certificate authority, like Thawt or GoDaddy, and received back a CRT or CER.  Instead of making a new KEY and CSR, use the one that you already have. In case the “Common Name” in your original CSR is not formatted EXACTLY like www.MyDomain.com:www.MyDomain.com (my mindful of the “:”), then you can always make a new CSR with your original KEY.)

First you need to create a key file. This is the file that is used to unlock your certificate once it is issued. (Make a note of the password used in the generation of the KEY file; you may need it later.)

openssl genrsa -out <KEYFILE NAME> 1024

example:
openssl genrsa -out lawrencefreenet.org.key 1024

Once this key is generated, you can create your certificate signing request to be transmitted to Intuit.

IMPORTANT: When creating this certificate the Common Name (Your Name) needs to be EXACTLY your.url:your.application.name. (For me, this was just www.MyDomain.com:www.MyDomain.com.)

openssl req -new -key <KEYFILE NAME> -out <CSRFILE NAME>

example:

openssl req -new -key /path/lawrencefreenet.org.key -out /tmp/lawrencefreenet.org.csr

Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:KS
Locality Name (eg, city) []:Lawrence
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Lawrence Freenet, Inc.
Organizational Unit Name (eg, section) []:
Common Name (eg, YOUR name) []:www.lawrencefreenet.org:gateway.lawrencefreenet.org
Email Address []:

Please enter the following ‘extra’ attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

IMPORTANT NOTE: The Email Address is (by default) attached to the CN. If you enter an email address, the certificate WILL BE REJECTED by Intuit’s servers.

Now that you have a certificate, open it up, copy the text and paste it to Intuit’s web tool at:

http://appreg.intuit.com

It will feed you back two certificates:

CA Certificate – this is used by your server to verify Intuit’s server. It can be added to your keystore. (Not sure this is necessary.) Your keystore is a list of all of the certificates which your computer considers to be valid. On our Debian Linux box this is located at:

/etc/ssl/certs/ca-certificates.crt

example (assumes you pasted the CA into the file /tmp/intuit.ca):
cat /tmp/intuit.ca >> /etc/ssl/certs/ca-certificates.crt

Intuit Certificate – this is the certificate presented by your application to the Intuit website to authenticate the system. It should be put in a SECURE place that your web server has access to.

For the purpose of this document, we have put this in the /tmp directory, but your setup may differ.

Example (Certificate is located at):
/tmp/intuit.pem

4. Getting a “Connection Ticket”
In order for you to connect to a merchant’s account on the Intuit server (yes, even for a test account) the Merchant needs to approve your application.

Now Intuit could have set this up so a code is generated for the merchant to enter (or cut and paste) into your application, but apparently they wanted to verify that the server running the software is indeed the server that is entered into Intuit’s application registration utility.

To facilitate this, they came up with a reasonably slick method of providing this authentication.

Basically, the merchant is sent to the Intuit website via a URL. The URL carries the following information:

1. The AppID of the application that needs to connect to Intuit’s servers (in our case it is 100019176 but yours will be different).
2. The Uniqe ID of the merchant as generated by your software. Let me explain this further. Say you are developing an application that you want to sell to Quickbooks users world wide. For you to bill these users, you would need to assign them a unique ID. Say Microsoft is a customer, you would issue them a Unique ID in your system (Say – 238463829). This ID would be sent to the Intuit servers in the URL. When the merchant authorizes your application the ID is sent BACK to your server (via an HTTP POST) along with the “connection ticket”. This allows your software to figure out which connection ticket goes with wich merchant.
a. If you only have one merchant (say you are developing an in-house application) then this data doesn’t matter.

So in our case we are going to send the user to the following URL.

https://login.ptc.quickbooks.com/j/qbn/sdkapp/confirm?appid=100019176&serviceid=1002&appdata=10001

(Replace the appid with your own appid, keep the same serviceid and you can remove the appdata part. NOTE: you have to register your app as “Test or PTC” for this to work. )

When we go to that URL we log in with the merchant account that we set up in step

1. Then we check the radio button for “payment.lawrencefreenet.org” (the description of your app) and hit “use this connection.

What this does is set off a script at Intuit that POSTS data to the URL we entered (https://www.lawrencefreenet.org/intuit.php).

Here is a listing of the script that we use to grab this data and write it to a file:(make sure you set the right permission for ticket.txt so intuit.php can write to it)

<?php
//This script is accessed by Intuit’s QBMS to return data from a request.

$PHP_ConnectionTicket = $_POST[‘conntkt’];
$PHP_AppData = $_POST[‘appdata’];
$PHP_AppID = $_POST[‘appid’];

$handle = fopen(“/tmp/ticket.txt”, “w”);
fwrite($handle, $PHP_ConnectionTicket.”\n”);
fwrite($handle, $PHP_AppData.”\n”);
fwrite($handle, $PHP_AppID.”\n”);

?>

What this does is create a file with the connection ticket in it. Once we have this connection ticket we are ready to go and start doing transactions.

To actually do test transactions we are going to need the ticket (TGT-…), the [intuit] certificate (the one saved as intuit.pem) and the key. In our case, we simply added the key to the certificate so that we don’t have to use a password or anything to load it. To do this we simply cat the key file into the certificate.

Example:
cat /path/lawrencefreenet.org.key >>/tmp/intuit.pem

cat tmp/secure.mydomain.com.key >> tmp/intuit.pem

This means you end up with the file intuit.pem, with the intuit certificate at the top of the file, and the original KEY certificate at the bottom of the file.  Supposedly, you don’t have to specify a password to CURL if you do it this way, but it doesn’t seem to work.

5. Running a test transaction.

Here is a listing for running a test transaction. This works well, it performs 2 transactions. The first one gets the session ticket using the connection ticket. Once this is obtained it runs a test transaction with the server.

<?php
/******************************************************************************

P R O C E S S C R E D I T C A R D S

******************************************************************************
******************************************************************************

S T A T I C V A R I A B L E S

******************************************************************************/

//QBMS Applicaiton Path
//Testing – https://webmerchantaccount.ptc…books.com/j/AppGateway
//Debug – https://webmerchantaccount.quickbooks.com/j/diag/http
//Production for Desktop = https://merchantaccount.quickbooks.com/j/AppGateway
//Production for hosted = https://webmerchantaccount.quickbooks.com/j/AppGateway

$PHP_ApplicationPath = “https://webmerchantaccount.ptc…books.com/j/AppGateway“;
//$PHP_ApplicationPath = “https://webmerchantaccount.quickbooks.com/j/diag/http“;

//Merchant Connection Ticket
$PHP_ConnectionTicket = ‘TGT-#############################’;

//Application Login – From Service Provider Account (NOT Merchant Account)
$PHP_ApplicationLogin = “gateway.lawrencefreenet.org”;

//Application ID
$PHP_AppID = “100019176”;

//SSL Cert
$PHP_Cert = ‘/tmp/intuit.pem’;

/******************************************************************************
******************************************************************************/

//This is the request that uses the merchant ticket to get the session ticket.
$PHP_QBMSXML[0] = ‘<?xml version=”1.0″ ?>
<?qbmsxml version=”2.0″?>
<QBMSXML>
<SignonMsgsRq>
<SignonAppCertRq>
<ClientDateTime>’.date(‘Y-m-d\TH:i:s’).'</ClientDateTime>
<ApplicationLogin>’.$PHP_ApplicationLogin.'</ApplicationLogin>
<ConnectionTicket>’.$PHP_ConnectionTicket.'</ConnectionTicket>
</SignonAppCertRq>
</SignonMsgsRq>
</QBMSXML>’;

$PHP_Data = FNC_do_call($PHP_ApplicationPath, $PHP_QBMSXML[0], $PHP_Cert);

echo “Results for ticket call:”.str_replace(“\n”, “<br>\n”, htmlentities($PHP_QBMSXML[0])).”<br><br><hr>”;

echo “Got Data: “.str_replace(“\n”, “<br>\n”, htmlentities($PHP_Data));

//Go ahead and get the session ticket
//Find the location of the start tag
$PHP_TempString = strstr($PHP_Data, “<SessionTicket>”);
$PHP_EndLocation = strpos($PHP_TempString, “</SessionTicket>”);
$PHP_SessionTicket = substr($PHP_TempString, 15, $PHP_EndLocation – 15);

//Debug
//echo “<hr>TempString = “.htmlentities($PHP_SessionTicket);

$PHP_QBMSXML[1] = ‘<?xml version=”1.0″ ?>
<?qbmsxml version=”2.0″?>
<QBMSXML>
<SignonMsgsRq>
<SignonTicketRq>
<ClientDateTime>’.date(‘Y-m-d\TH:i:s’).'</ClientDateTime>
<SessionTicket>’.$PHP_SessionTicket.'</SessionTicket>
</SignonTicketRq>
</SignonMsgsRq>
<QBMSXMLMsgsRq>
<CustomerCreditCardAuthRq requestID=”23909”>
<TransRequestID>E09C86CF</TransRequestID>
<CreditCardNumber>4111111111111111</CreditCardNumber>
<ExpirationMonth>12</ExpirationMonth>
<ExpirationYear>2008</ExpirationYear>
<IsECommerce>true</IsECommerce>
<Amount>203.00</Amount>
<CreditCardAddress>23 Garcia Ave</CreditCardAddress>
<CreditCardPostalCode>94043</CreditCardPostalCode>
</CustomerCreditCardAuthRq>
</QBMSXMLMsgsRq>
</QBMSXML>’;

$PHP_Data = FNC_do_call($PHP_ApplicationPath, $PHP_QBMSXML[1], $PHP_Cert);

echo “<hr><hr>Results for ticket call:”.str_replace(“\n”, “<br>\n”, htmlentities($PHP_QBMSXML[1])).”<br><br><hr>”;

echo “Got Data: “.str_replace(“\n”, “<br>\n”, htmlentities($PHP_Data));

function FNC_do_call($PHP_URL, $PHP_Request, $PHP_Cert) {

$PHP_Header[] = “Content-type: application/x-qbmsxml”;
$PHP_Header[] = “Content-length: “.strlen($PHP_Request);

$handle = fopen(“/tmp/curlerrors.txt”, “w”);

$ch = curl_init();
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, ‘POST’);
curl_setopt($ch, CURLOPT_URL, $PHP_URL);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $PHP_Header);
curl_setopt($ch, CURLOPT_POSTFIELDS, $PHP_Request);
curl_setopt($ch, CURLOPT_STDERR, $handle);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSLCERT, $PHP_Cert);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, “ElvisLives”); // I had to hard code my password in order to keep CURL from complaining that it “couldn’t find my key”

$data = curl_exec($ch);

if (curl_errno($ch)) {

die(“Error = “.curl_error($ch));

} else {

curl_close($ch);

}

return $data;

}
?>

Testing Credit Card Transactions : Testing Credit Card Transactions

Testing Credit Card Transactions
When your application is using the QBMS test environment (.ptc), all of the transation requests are sent to the QBMS emulation backend. This emulator “processes” the requests and returns the appropriate responses. The word “process” is in quotes because there is no real processing going on. The card number is checked against the valid card numbers listed below (Table 6-1), and the expiration date is checked against the current date.
Table 6-1 Valid Test Credit Card numbers
Card Type Test Number Number of Characters
Master Card 5105105105105100 (16)Characters
Master Card 5555555555554444 (16)Characters
VISA 4222222222222 (13)Characters
VISA 4111111111111111 (16)Characters
VISA 4012888888881881 (16)Characters
American Express 378282246310005 (15)Characters
American Express 371449635398431 (15)Characters
Amex Corporate 378734493671000 (15)Characters
Diners Club 38520000023237 (14)Characters
Diners Club 30569309025904 (14)Characters
Discover 6011111111111117 (16)Characters
Discover 6011000990139424 (16)Characters
Note: Currently, there is no way to test voice authorization requests in the QBMS test environment.
However, the AVS values and CVS values are not checked, nor account limits, nor status, and so forth. To cause the appropriate errors to be returned in response XML, however, you can supply a configID value within the <NameOnCard> tag that will cause the error you want returned:
<NameOnCard>configid=value </NameOnCard>
Simply replace “value in the above line with one of the ConfigID values listeds in Table 6-2.
Table 6-2 ConfigID values and the errors they generate:
Error to be Returned ConfigID value to insert Error Emulated
10200 10200_comm An error occurred while communicating with the credit card processing gateway.
10201 10201_login An error occurred during login to the processing gateway.
10301 10301_ccinvalid This credit card account number is invalid.
10400 10400_insufffunds This account does not have sufficient funds to process this transaction.
10401 10401_decline The request to process this transaction has been declined.
10403 10403_acctinvalid The merchant account information submitted is not recognized.
10404 10404_referral This transaction has been declined, but can be approved by obtaining a Voice Authorization code from the card issuer.
10405 10405_void An error occurred while attempting to void this transaction.
10406 10406_capture An error occurred while processing the capture transaction.
10500 10500_general A general error occurred at the credit card processing gateway.
10000 10000_avscvdfail Status OK, AVS Street and Zip fail, card security code fail
10000 Is default: supply no configID value. Status OK, AVS Street and Zip pass, card security code pass (AVS and CSC fields supplied in the request)
10000 Is default: supply no configID value Status OK, AVS Street and Zip unavailable, card security code unavailable (used only required fields in the xml request)
For example, in the following request, a credit card charge transaction is requested, with the NameOnCard tag set to generate an insufficient funds error:

<QBMSXML>
<QBMSXMLMsgsRq>
<CustomerCreditCardChargeRq>
<TransRequestID>02B123451</TransRequestID>
<CreditCardNumber>5555555555554444</CreditCardNumber>
<ExpirationMonth>12</ExpirationMonth>
<ExpirationYear>2008</ExpirationYear>
<Amount>130.00</Amount>
<NameOnCard>configid=10400_insufffunds</NameOnCard>
</CustomerCreditCardChargeRq>
</QBMSXMLMsgsRq>
</QBMSXML>

  • QBMS Resources:  https://developer.intuit.com/qbms/support/?id=1026
PHP QBMS EXAMPLE

4 thoughts on “PHP QBMS EXAMPLE

  • October 6, 2009 at 4:43 pm
    Permalink

    Thanks for the great information! I am attempting this same process, and I’ve gotten as far as receiving the two certificates from Intuit. But now, after trying to implement them on my server and using a PHP script to call Intuit’s server, I get this error:

    Error = error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca

    Ever seen this error before, or do you have any idea what it means?

  • October 15, 2009 at 4:41 pm
    Permalink

    Thanks for this post Eon. I’ve spent many hours trying to piece together some of the steps you have laid out here.
    Thanks,
    Adam

  • October 25, 2009 at 12:37 am
    Permalink

    Added note: You’ll need to actually set up a production server to test the address verification. Which is kind of painful to see 10+ 2 cents charges on your test credit card. The time out thing was also annoying. If you have 2 credit card submission from the same user, it’s treated as one. I had to send something different, dont remember now.

    Sometimes the AVS verfication is not avilable, you have to decide to pass or deny the transaction.

  • February 1, 2010 at 4:41 pm
    Permalink

    I couldn’t seem to get QBMS to post to my script. I could do it manually, but that is where it ended. Since this was only going to be for our own company use I really only need to get the connection ticket a couple of times. So when I went to appreg and registered my application, I set it to a desktop app first. This gave me my connection ticket. I then set it back to a web application. This allowed my to set up my php script to submit payments and get any error messages back without a problem.

Leave a Reply

Your email address will not be published. Required fields are marked *