PurchaseRequest   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 265
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 26
lcom 3
cbo 4
dl 0
loc 265
ccs 92
cts 92
cp 1
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getProfileId() 0 4 1
A setProfileId() 0 4 1
A getSecretKey() 0 4 1
A setSecretKey() 0 4 1
A getAccessKey() 0 4 1
A setAccessKey() 0 4 1
A getReferenceNumber() 0 5 2
A setReferenceNumber() 0 4 1
A getLocale() 0 5 2
A setLocale() 0 4 1
A getTransactionType() 0 4 1
C getData() 0 111 10
A sendData() 0 10 1
A getEndpoint() 0 4 2
1
<?php
2
3
namespace Omnipay\CyberSource\Message;
4
5
use Omnipay\Common\Message\AbstractRequest;
6
7
/**
8
 * CyberSource Purchase Request
9
 */
10
class PurchaseRequest extends AbstractRequest
11
{
12
    /** @var string */
13
    protected $liveEndpoint = 'https://secureacceptance.cybersource.com/pay';
14
    /** @var string */
15
    protected $testEndpoint = 'https://testsecureacceptance.cybersource.com/pay';
16
    /** @var string  Override this if using inherited class for additional transaction types */
17
    protected $transactionType = 'sale';
18
19
    /**
20
     * Get the profile ID for the merchant account
21
     *
22
     * @return string
23
     */
24 6
    public function getProfileId()
25
    {
26 6
        return $this->getParameter('profileId');
27
    }
28
29
    /**
30
     * Set the profile ID for the merchant account
31
     *
32
     * @param string $value  ASCII Alphanumeric + punctuation string, maximum 36 characters
33
     *
34
     * @return AbstractRequest
35
     */
36 16
    public function setProfileId($value)
37
    {
38 16
        return $this->setParameter('profileId', $value);
39
    }
40
41
    /**
42
     * Get the secret key for the merchant account
43
     *
44
     * @return string
45
     */
46 8
    public function getSecretKey()
47
    {
48 8
        return $this->getParameter('secretKey');
49
    }
50
51
    /**
52
     * Set the secret key for the merchant account
53
     *
54
     * @param string $value  Alphanumeric string, maximum 32 characters
55
     *
56
     * @return AbstractRequest
57
     */
58 16
    public function setSecretKey($value)
59
    {
60 16
        return $this->setParameter('secretKey', $value);
61
    }
62
63
    /**
64
     * Get the access key for the merchant account
65
     *
66
     * @return string
67
     */
68 6
    public function getAccessKey()
69
    {
70 6
        return $this->getParameter('accessKey');
71
    }
72
73
    /**
74
     * Set the access key for the merchant account
75
     *
76
     * @param string $value  Alphanumeric string, maximum 32 characters
77
     *
78
     * @return AbstractRequest
79
     */
80 16
    public function setAccessKey($value)
81
    {
82 16
        return $this->setParameter('accessKey', $value);
83
    }
84
85
    /**
86
     * Optional merchant sale reference number, falls back to using the transaction ID if not set
87
     *
88
     * @return string
89
     */
90 5
    public function getReferenceNumber()
91
    {
92 5
        $reference = $this->getParameter('referenceNumber');
93 5
        return empty($reference) ? $this->getTransactionId() : $reference;
94
    }
95
96
    /**
97
     * Set the unique merchant-generated order reference or tracking number for each transaction.
98
     *
99
     * @param string $value  Reference to use
100
     */
101 1
    public function setReferenceNumber($value)
102
    {
103 1
        return $this->setParameter('referenceNumber', $value);
104
    }
105
106
    /**
107
     * Get the locale set for this request, falls back to using 'en' if not set
108
     *
109
     * @return string
110
     */
111 5
    public function getLocale()
112
    {
113 5
        $locale = $this->getParameter('locale');
114 5
        return empty($locale) ? 'en' : $locale;
115
    }
116
117
    /**
118
     * Set the locale for this request
119
     *
120
     * @param string  $value  ISO formatted string indicating language and country e.g. en-nz
121
     */
122 1
    public function setLocale($value)
123
    {
124 1
        return $this->setParameter('locale', $value);
125
    }
126
127
    /**
128
     * Get the transaction type
129
     *
130
     * Can be one of:
131
     *  - authorization
132
     *  - authorization,create_payment_token
133
     *  - authorization,update_payment_token
134
     *  - sale
135
     *  - sale,create_payment_token
136
     *  - sale,update_payment_token
137
     *  - create_payment_token
138
     *  - update_payment_token
139
     *
140
     * @return string
141
     */
142 4
    public function getTransactionType()
143
    {
144 4
        return $this->transactionType;
145
    }
146
147 4
    public function getData()
148
    {
149 4
        $this->validate(
150 4
            'profileId',
151 4
            'accessKey',
152
            // the secret key is not supplied in the data, but is required for generating the signature
153 4
            'secretKey',
154 4
            'amount',
155 4
            'currency'
156
        );
157
158
        // mandatory fields
159
        $data = array(
160 4
            'access_key' => $this->getAccessKey(),
161 4
            'amount' => $this->getAmount(),
162
            // uses ISO-4217 codes
163 4
            'currency' => $this->getCurrency(),
164 4
            'locale' => $this->getLocale(),
165 4
            'profile_id' => $this->getProfileId(),
166
            // no duplicate checking on this (falls back to transaction ID if not set)
167 4
            'reference_number' => $this->getReferenceNumber(),
168
            // ISO 8601 date in UTC
169 4
            'signed_date_time' => gmdate("Y-m-d\TH:i:s\Z"),
170 4
            'transaction_type' => $this->getTransactionType(),
171
            // merchant-generated transaction number used for duplicate checking
172 4
            'transaction_uuid' => $this->getTransactionId(),
173
        );
174
175
        // optional fields
176
        $optional_data = array(
177 4
            'customer_ip_address' => $this->getClientIp(),
178
            // merchant defined data 1-4 are stored with tokens, so transaction-specific data from 5 onwards
179 4
            'merchant_defined_data5' => $this->getDescription(),
180 4
            'override_backoffice_post_url' => $this->getNotifyUrl(),
181 4
            'override_custom_cancel_page' => $this->getCancelUrl(),
182
            // signing this field is actually required if present, despite what the integration docs say
183 4
            'override_custom_receipt_page' => $this->getReturnUrl(),
184
        );
185
186
        // billing details
187 4
        $card = $this->getCard();
188 4
        if ($card) {
189 3
            $country = $card->getCountry();
190 3
            $state = $card->getState();
191 3
            $post_code = $card->getPostcode();
192
            // US/CA have strict state/post code validators if the fields are present
193
            // better set blank here (and filtered out further down) than wrong as can be filled out at gateway end
194 3
            if ($country == 'US' || $country == 'CA') {
195 2
                $state = substr(strtoupper($state), 0, 2);
196 2
                if ($country == 'US') {
197 1
                    if (preg_match('/^[0-9]{5}-[0-9]{4}$/', $post_code) !== 1) {
198 1
                        $post_code = '';
199
                    }
200 1
                } elseif ($country == 'CA') {
201 1
                    $post_code = strtoupper($post_code);
202
                    // Strictly, CA Post is '/^[ABCEGHJ-NPRSTVXY][0-9][ABCEGHJ-NPRSTV-Z] [0-9][ABCEGHJ-NPRSTV-Z][0-9]$/'
203 1
                    if (preg_match('/^[A-Z][0-9][A-Z] [0-9][A-Z][0-9]$/', $post_code) !== 1) {
204 2
                        $post_code = '';
205
                    }
206
                }
207
            } else {
208 1
                $state = substr($state, 0, 60);
209 1
                $post_code = substr($post_code, 0, 10);
210
            }
211
            $optional_data += array(
212 3
                'bill_to_forename' => $card->getFirstName(),
213 3
                'bill_to_surname' => $card->getLastName(),
214 3
                'bill_to_address_line1' => $card->getAddress1(),
215 3
                'bill_to_address_line2' => $card->getAddress2(),
216 3
                'bill_to_address_city' => $card->getCity(),
217 3
                'bill_to_address_state' => $state,
218 3
                'bill_to_address_postal_code' => $post_code,
219 3
                'bill_to_address_country' => $country,
220 3
                'bill_to_email' => $card->getEmail(),
221 3
                'bill_to_phone' => $card->getPhone(),
222
            );
223
        }
224
225
        // item details
226 4
        $items = $this->getItems();
227 4
        if ($items) {
228
            // having any item details forces line_item_count to be required
229
            $optional_data += array(
230 3
                'line_item_count' => count($items),
231
            );
232 3
            foreach ($items as $n => $item) {
233
                $optional_data += array(
234 3
                    "item_{$n}_name" => $item->getName(),
235 3
                    "item_{$n}_quantity" => $item->getQuantity(),
236 3
                    "item_{$n}_unit_price" => $this->formatCurrency($item->getPrice()),
237
                );
238
                // @todo if we want to add additional fields, need to create CyberSourceItem and CyberSourceItemBag
239
                // if ($item instanceof CyberSourceItem) {
240
                //     "item_{$n}_code" => $item->getCode(),
241
                //     "item_{$n}_sku" => $item->getSku(),
242
                //     "item_{$n}_tax_amount" => $this->formatCurrency($item->getTax()),
243
                // }
244
            }
245
        }
246
247
        // omit any optional parameters that aren't set
248 4
        $optional_data = array_filter($optional_data);
249
        // merge data
250 4
        $data += $optional_data;
251
252
        // rather than working out peculiar loopholes in the integration documentation, just sign everything
253 4
        $data['unsigned_field_names'] = '';
254 4
        $data['signed_field_names'] = implode(',', array_keys($data)).',signed_field_names';
255
256 4
        return $data;
257
    }
258
259 1
    public function sendData($data)
260
    {
261 1
        $security = new Security();
262 1
        $data['signature'] = $security->createSignature(
263 1
            $data,
264 1
            explode(',', $data['signed_field_names']),
265 1
            $this->getSecretKey()
266
        );
267 1
        return $this->response = new PurchaseResponse($this, $data);
268
    }
269
270 2
    public function getEndpoint()
271
    {
272 2
        return $this->getTestMode() ? $this->testEndpoint : $this->liveEndpoint;
273
    }
274
}
275