/mvc/components/cc
[return to app]1
<?php
2 /**
3 * Credit Card functions
4 */
5 class ccComponent {
6 /**
7 * These properties store the response from AuthorizeNet after a charge
8 *
9 * @var string
10 */
11 public $authnetResponse, $authnetResponseText, $authnetApprovalCode, $authnetTransactionId;
12
13 /**
14 * Every second digit in the Mod 10 (Luhn) algorithm gets doubled and then the resulting digit(s) are added
together
15 * This array maps the orignal number to the digit after the Mod 10 equation
16 */
17 protected $_mod10digits = array(0 => 0, 1 => 2, 2 => 4, 3 => 6, 4 => 8, 5 => 1, 6 => 3, 7 => 5, 8 => 7, 9 =>
9);
18
19 /**
20 * Standard credit card test numbers; these will validate Mod 10 but are never valid credit card accounts
21 *
22 * @var array
23 */
24 public $ccTestNumbers = array(5105105105105100, 5555555555554444, 4222222222222, 4111111111111111,
25 4012888888881881, 378282246310005, 371449635398431, 378734493671000,
26 38520000023237, 30569309025904, 6011111111111117, 6011000990139424,
27 3530111333300000, 3566002020360505);
28
29 /**
30 * Credit card types; keys mapped to the AuthorizeNet card type response
31 *
32 * @var array
33 */
34 public $cardTypes = array(4 => 'Visa', 5 => 'MasterCard', 3 => 'American Express', 6 => 'Discover Card',
35 8 => 'Diners Club', 1 => 'JCB (Japanese Credit Bureau)', 7 => 'Australian
BankCard',
36 2 => 'enRoute');
37
38 /**
39 * Validates the length of credit card number is correct for the card type
40 *
41 * @param int $cc
42 * @param int $cardType This must match the key from the $this->cardTypes array
43 * @return boolean
44 */
45 public function isValidCcLength($cc, $cardType) {
46 $length[16] = array(1, 4, 5, 6, 7); //JCB, Visa, MasterCard, Discover Card, Australian BankCard
47 $length[15] = array(1, 2, 3); //JCB, enRoute, American Express
48 $length[14] = array(8); //Diners Club
49 $length[13] = array(4); //Visa
50 $ccLength = strlen($cc);
51 return (isset($length[$ccLength]) && in_array($cardType, $length[$ccLength]));
52 }
53
54 /**
55 * Flag set by $this->getCardType() if a Diners Club card is a CarteBlanch card
56 *
57 * @var boolean
58 */
59 public $isCarteBlanch;
60
61 /**
62 * Determines credit card type by the credit card number
63 *
64 * @param int $cc Can be the first four digits of the credit card number, the entire number or anything in
between
65 * @return int Matches the keys in $this->cardTypes
66 */
67 public function getCardType($cc, $returnHumanReadable = true) {
68 $cardType = 0;
69 $prefix = substr($cc, 0, 4);
70 if ($prefix >= 4000 && $prefix <= 4999) {
71 $cardType = 4; //Visa
72 } else if ($prefix >= 5100 && $prefix <= 5599) {
73 $cardType = 5; //MasterCard
74 } else if (($prefix >= 3400 && $prefix <= 3499) || ($prefix >= 3700 && $prefix <= 3799)) {
75 $cardType = 3; //American Express
76 } else if (($prefix >= 3000 && $prefix <= 3059) || ($prefix >= 3600 && $prefix <= 3699)
77 || ($prefix >= 3800 && $prefix <= 3899)) {
78 $cardType = 8; //Diners Club
79 $this->isCarteBlanch = ($prefix >= 3890 && $prefix <= 3899);
80 } else if ($prefix >= 3528 && $prefix <= 3589) {
81 $cardType = 1; //JCB
82 } else {
83 switch ($prefix) {
84 case 6011:
85 $cardType = 6; //Discover Card
86 break;
87 case 1800:
88 case 2131:
89 $cardType = 1; //JCB
90 break;
91 case 2014:
92 case 2149:
93 $cardType = 2; //enRoute
94 break;
95 case 5610:
96 $cardType = 7; //Australian BankCard
97 break;
98 }
99 }
100 if ($cardType && $returnHumanReadable && isset($this->cardTypes[$cardType])) {
101 $cardType = $this->cardTypes[$cardType];
102 }
103 return $cardType;
104 }
105
106 /**
107 * Verifies if a credit card number passes the Mod 10 specification
108 *
109 * @param int $cc
110 * @return boolean
111 */
112 public function isMod10Valid($cc) {
113 $cclen = strlen($cc);
114 if ($cclen > 16 || $cclen < 13 || !is_numeric($cc)) {
115 return false;
116 }
117 $digitsum = 0;
118 $currentbit = 1;
119 $startbit = ($cclen % 2);
120 for ($x = 0; $x < $cclen; $x++) {
121 $currentbit =! $currentbit;
122 if ($currentbit == $startbit) {
123 $cc[$x] = $this->_mod10digits[$cc[$x]];
124 }
125 $digitsum += $cc[$x];
126 }
127 return (boolean) !($digitsum % 10);
128 }
129
130 /**
131 * Charges a credit card through AuthorizeNet and records meta data in properties:
132 * $this->authnetResponse, $this->authnetResponseText, $this->authnetApprovalCode and
$this->authnetTransactionId
133 *
134 * Requires AUTHNET_LOGIN and AUTHNET_PASSWORD to be set in the .config
135 *
136 * @param float $amount
137 * @param int $cc
138 * @param string $exp
139 * @return int 1=approved, 2=declined, 3=error
140 */
141 public function chargeAuthNet($amount, $cc, $exp) {
142 $postfields = array('x_login' => get::$config->AUTHNET_LOGIN,
143 'x_password' => get::$config->AUTHNET_PASSWORD,
144 'x_version' => '3.1',
145 'x_delim_data' => 'TRUE',
146 'x_method' => 'CC',
147 'x_type' => 'AUTH_CAPTURE',
148 'x_customer_ip' => $_SERVER['REMOTE_ADDR'],
149 'x_amount' => $amount,
150 'x_card_num' => $cc,
151 'x_exp_date' => $exp);
152
153 $postdata = http_build_query($postfields);
154 $sockheader = 'POST /gateway/transact.dll HTTP/1.0' . PHP_EOL
155 . 'Host: secure.authorize.net' . PHP_EOL
156 . 'Content-type: application/x-www-form-urlencoded' . PHP_EOL
157 . 'Content-length: ' . strlen($postdata) . PHP_EOL
158 . 'Accept: */*';
159
160 $fp = stream_socket_client('ssl://secure.authorize.net:443', $errno, $errstr, 30);
161 fwrite($fp, $sockheader . PHP_EOL . PHP_EOL . $postdata . PHP_EOL . PHP_EOL);
162 while(trim(fgets($fp, 128)));
163 //error-suppression is needed for connecting to older IIS servers that do not close the connection
properly
164 $responsestring = @fgets($fp);
165 fclose($fp);
166
167 $this->authnetResponse = explode(',', $responsestring);
168 $this->authnetResponseText = $this->authnetResponse[3];
169 $this->authnetApprovalCode = $this->authnetResponse[4];
170 $this->authnetTransactionId = $this->authnetResponse[6];
171 return $this->authnetResponse[0];
172 }
173
174 /**
175 * Redirect a user to a Google Payment checkout prepopulated with their order (& directing payment to your
account)
176 *
177 * @param array $items
178 */
179 public function redirectToGoogleCheckout(array $items) {
180 $data = '<?xml version="1.0" encoding="UTF-8"?>
181 <checkout-shopping-cart xmlns="http://checkout.google.com/schema/2">
182 <shopping-cart>
183 <items>';
184 foreach ($items as $item) {
185 $currency = (!isset($item['currency']) ? 'USD' : $item['currency']);
186 $data .= '
187 <item>
188 <item-name>' . $item['name'] . '</item-name>
189 <item-description>' . $item['description'] . '</item-description>
190 <unit-price currency="' . $currency . '">' . $item['price'] . '</unit-price>
191 <quantity>' . (!isset($item['quantity']) ? 1 : $item['quantity']) . '</quantity>
192 </item>';
193 }
194 $data .= '
195 </items>
196 </shopping-cart>
197 <checkout-flow-support>
198 <merchant-checkout-flow-support/>
199 </checkout-flow-support>
200 </checkout-shopping-cart>
201 ';
202 if (!get::$config->GOOGLE_CHECKOUT['useSandbox']) {
203 $credentials = get::$config->GOOGLE_CHECKOUT['live'];
204 $url = 'checkout.google.com/api/checkout/v2/request/Merchant/';
205 } else {
206 $credentials = get::$config->GOOGLE_CHECKOUT['sandbox'];
207 $url = 'sandbox.google.com/checkout/api/checkout/v2/request/Merchant/';
208 }
209 $url = 'https://' . $credentials['id'] . ':' . $credentials['key'] . '@' . $url . $credentials['id'];
210 $errorLevel = error_reporting(E_ERROR);
211 $xml = get::component('post')->postRequest($url, $data);
212 error_reporting($errorLevel);
213 if ($xml) {
214 $url = trim(html_entity_decode(strip_tags($xml)));
215 load::redirect($url);
216 } else {
217 error_log('No Google Payment URL returned from: ' . $data);
218 }
219 }
220 }