├── .gitignore
├── Paycom
├── Application.php
├── Database.php
├── Format.php
├── Merchant.php
├── Order.php
├── PaycomException.php
├── Request.php
├── Response.php
└── Transaction.php
├── README.md
├── composer.json
├── functions.php
├── index.php
├── password.paycom
└── paycom.config.sample.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor/
3 | paycom.config.php
4 |
--------------------------------------------------------------------------------
/Paycom/Application.php:
--------------------------------------------------------------------------------
1 | merchant_id, login, keyFile keys.
17 | */
18 | public function __construct($config)
19 | {
20 | $this->config = $config;
21 | $this->request = new Request();
22 | $this->response = new Response($this->request);
23 | $this->merchant = new Merchant($this->config);
24 | $this->db = new Database($this->config);
25 | $this->db_conn = $this->db->new_connection();
26 | }
27 |
28 | /**
29 | * Authorizes session and handles requests.
30 | */
31 | public function run()
32 | {
33 | // authorize session
34 | $this->merchant->Authorize($this->request->id);
35 |
36 | // handle request
37 | try {
38 | switch ($this->request->method) {
39 | case 'CheckPerformTransaction':
40 | $this->CheckPerformTransaction();
41 | break;
42 | case 'CheckTransaction':
43 | $this->CheckTransaction();
44 | break;
45 | case 'CreateTransaction':
46 | $this->CreateTransaction();
47 | break;
48 | case 'PerformTransaction':
49 | $this->PerformTransaction();
50 | break;
51 | case 'CancelTransaction':
52 | $this->CancelTransaction();
53 | break;
54 | case 'ChangePassword':
55 | $this->ChangePassword();
56 | break;
57 | case 'GetStatement':
58 | $this->GetStatement();
59 | break;
60 | default:
61 | $this->response->error(
62 | PaycomException::ERROR_METHOD_NOT_FOUND,
63 | 'Method not found.',
64 | $this->request->method
65 | );
66 | break;
67 | }
68 | } catch (PaycomException $exc) {
69 | $exc->send();
70 | }
71 | }
72 |
73 | private function CheckPerformTransaction()
74 | {
75 | $order = new Order($this->request->id, $this->request->params);
76 | $order->find($this->request->params['account']);
77 |
78 | // validate parameters
79 | $order->validate($this->request->params);
80 |
81 | // todo: Check is there another active or completed transaction for this order
82 | $transaction = new Transaction($this->db_conn);
83 | $found = $transaction->find($this->request->params);
84 | if ($found && ($found->state == Transaction::STATE_CREATED || $found->state == Transaction::STATE_COMPLETED)) {
85 | $this->response->error(
86 | PaycomException::ERROR_COULD_NOT_PERFORM,
87 | 'There is other active/completed transaction for this order.'
88 | );
89 | }
90 |
91 | // if control is here, then we pass all validations and checks
92 | // send response, that order is ready to be paid.
93 | $this->response->send(['allow' => true]);
94 | }
95 |
96 | private function CheckTransaction()
97 | {
98 | // todo: Find transaction by id
99 | $transaction = new Transaction($this->db_conn);
100 | $found = $transaction->find($this->request->params);
101 | if (!$found) {
102 | $this->response->error(
103 | PaycomException::ERROR_TRANSACTION_NOT_FOUND,
104 | 'Transaction not found.'
105 | );
106 | }
107 |
108 | // todo: Prepare and send found transaction
109 | $this->response->send([
110 | 'create_time' => Format::datetime2timestamp($found->create_time),
111 | 'perform_time' => Format::datetime2timestamp($found->perform_time),
112 | 'cancel_time' => Format::datetime2timestamp($found->cancel_time),
113 | 'transaction' => $found->id,
114 | 'state' => $found->state,
115 | 'reason' => isset($found->reason) ? 1 * $found->reason : null
116 | ]);
117 | }
118 |
119 | private function CreateTransaction()
120 | {
121 | $order = new Order($this->request->id, $this->request->params);
122 | $order->find($this->request->params['account']);
123 |
124 | // validate parameters
125 | $order->validate($this->request->params);
126 |
127 | // todo: Find transaction by id
128 | $transaction = new Transaction($this->db_conn);
129 | $found = $transaction->find($this->request->params);
130 |
131 | if ($found) {
132 | if ($found->state != Transaction::STATE_CREATED) { // validate transaction state
133 | $this->response->error(
134 | PaycomException::ERROR_COULD_NOT_PERFORM,
135 | 'Transaction found, but is not active.'
136 | );
137 | } elseif ($found->isExpired()) { // if transaction timed out, cancel it and send error
138 | $found->cancel(Transaction::REASON_CANCELLED_BY_TIMEOUT);
139 | $this->response->error(
140 | PaycomException::ERROR_COULD_NOT_PERFORM,
141 | 'Transaction is expired.'
142 | );
143 | } else { // if transaction found and active, send it as response
144 | $this->response->send([
145 | 'create_time' => Format::datetime2timestamp($found->create_time),
146 | 'transaction' => $found->id,
147 | 'state' => $found->state,
148 | 'receivers' => $found->receivers
149 | ]);
150 | }
151 | } else { // transaction not found, create new one
152 |
153 | // validate new transaction time
154 | if (Format::timestamp2milliseconds(1 * $this->request->params['time']) - Format::timestamp(true) >= Transaction::TIMEOUT) {
155 | $this->response->error(
156 | PaycomException::ERROR_INVALID_ACCOUNT,
157 | PaycomException::message(
158 | 'С даты создания транзакции прошло ' . Transaction::TIMEOUT . 'мс',
159 | 'Tranzaksiya yaratilgan sanadan ' . Transaction::TIMEOUT . 'ms o`tgan',
160 | 'Since create time of the transaction passed ' . Transaction::TIMEOUT . 'ms'
161 | ),
162 | 'time'
163 | );
164 | }
165 |
166 | // create new transaction
167 | // keep create_time as timestamp, it is necessary in response
168 | $create_time = Format::timestamp();
169 | $transaction->paycom_transaction_id = $this->request->params['id'];
170 | $transaction->paycom_time = $this->request->params['time'];
171 | $transaction->paycom_time_datetime = Format::timestamp2datetime($this->request->params['time']);
172 | $transaction->create_time = Format::timestamp2datetime($create_time);
173 | $transaction->state = Transaction::STATE_CREATED;
174 | $transaction->amount = $this->request->amount;
175 | $transaction->order_id = $this->request->account('order_id');
176 | $transaction->save(); // after save $transaction->id will be populated with the newly created transaction's id.
177 |
178 | // send response
179 | $this->response->send([
180 | 'create_time' => $create_time,
181 | 'transaction' => $transaction->id,
182 | 'state' => $transaction->state,
183 | 'receivers' => null
184 | ]);
185 | }
186 | }
187 |
188 | private function PerformTransaction()
189 | {
190 | $transaction = new Transaction($this->db_conn);
191 | // search transaction by id
192 | $found = $transaction->find($this->request->params);
193 |
194 | // if transaction not found, send error
195 | if (!$found) {
196 | $this->response->error(PaycomException::ERROR_TRANSACTION_NOT_FOUND, 'Transaction not found.');
197 | }
198 |
199 | switch ($found->state) {
200 | case Transaction::STATE_CREATED: // handle active transaction
201 | if ($found->isExpired()) { // if transaction is expired, then cancel it and send error
202 | $found->cancel(Transaction::REASON_CANCELLED_BY_TIMEOUT);
203 | $this->response->error(
204 | PaycomException::ERROR_COULD_NOT_PERFORM,
205 | 'Transaction is expired.'
206 | );
207 | } else { // perform active transaction
208 | // todo: Mark order/service as completed
209 | $params = ['order_id' => $found->order_id];
210 | $order = new Order($this->request->id);
211 | $order->find($params);
212 | $order->changeState(Order::STATE_PAY_ACCEPTED);
213 |
214 | // todo: Mark transaction as completed
215 | $perform_time = Format::timestamp();
216 | $found->state = Transaction::STATE_COMPLETED;
217 | $found->perform_time = Format::timestamp2datetime($perform_time);
218 | $found->save();
219 |
220 | $this->response->send([
221 | 'transaction' => $found->id,
222 | 'perform_time' => $perform_time,
223 | 'state' => $found->state
224 | ]);
225 | }
226 | break;
227 |
228 | case Transaction::STATE_COMPLETED: // handle complete transaction
229 | // todo: If transaction completed, just return it
230 | $this->response->send([
231 | 'transaction' => $found->id,
232 | 'perform_time' => Format::datetime2timestamp($found->perform_time),
233 | 'state' => $found->state
234 | ]);
235 | break;
236 |
237 | default:
238 | // unknown situation
239 | $this->response->error(
240 | PaycomException::ERROR_COULD_NOT_PERFORM,
241 | 'Could not perform this operation.'
242 | );
243 | break;
244 | }
245 | }
246 |
247 | private function CancelTransaction()
248 | {
249 | $transaction = new Transaction($this->db_conn);
250 |
251 | // search transaction by id
252 | $found = $transaction->find($this->request->params);
253 |
254 | // if transaction not found, send error
255 | if (!$found) {
256 | $this->response->error(PaycomException::ERROR_TRANSACTION_NOT_FOUND, 'Transaction not found.');
257 | }
258 |
259 | switch ($found->state) {
260 | // if already cancelled, just send it
261 | case Transaction::STATE_CANCELLED:
262 | case Transaction::STATE_CANCELLED_AFTER_COMPLETE:
263 | $this->response->send([
264 | 'transaction' => $found->id,
265 | 'cancel_time' => Format::datetime2timestamp($found->cancel_time),
266 | 'state' => $found->state
267 | ]);
268 | break;
269 |
270 | // cancel active transaction
271 | case Transaction::STATE_CREATED:
272 | // cancel transaction with given reason
273 | $found->cancel(1 * $this->request->params['reason']);
274 | // after $found->cancel(), cancel_time and state properties populated with data
275 |
276 | // change order state to cancelled
277 | $order = new Order($this->request->id);
278 | $order->find($this->request->params);
279 | $order->changeState(Order::STATE_CANCELLED);
280 |
281 | // send response
282 | $this->response->send([
283 | 'transaction' => $found->id,
284 | 'cancel_time' => Format::datetime2timestamp($found->cancel_time),
285 | 'state' => $found->state
286 | ]);
287 | break;
288 |
289 | case Transaction::STATE_COMPLETED:
290 | // find order and check, whether cancelling is possible this order
291 | $order = new Order($this->request->id);
292 | $order->find($this->request->params);
293 | if ($order->allowCancel()) {
294 | // cancel and change state to cancelled
295 | $found->cancel(1 * $this->request->params['reason']);
296 | // after $found->cancel(), cancel_time and state properties populated with data
297 |
298 | $order->changeState(Order::STATE_CANCELLED);
299 |
300 | // send response
301 | $this->response->send([
302 | 'transaction' => $found->id,
303 | 'cancel_time' => Format::datetime2timestamp($found->cancel_time),
304 | 'state' => $found->state
305 | ]);
306 | } else {
307 | // todo: If cancelling after performing transaction is not possible, then return error -31007
308 | $this->response->error(
309 | PaycomException::ERROR_COULD_NOT_CANCEL,
310 | 'Could not cancel transaction. Order is delivered/Service is completed.'
311 | );
312 | }
313 | break;
314 | }
315 | }
316 |
317 | private function ChangePassword()
318 | {
319 | // validate, password is specified, otherwise send error
320 | if (!isset($this->request->params['password']) || !trim($this->request->params['password'])) {
321 | $this->response->error(PaycomException::ERROR_INVALID_ACCOUNT, 'New password not specified.', 'password');
322 | }
323 |
324 | // if current password specified as new, then send error
325 | if ($this->merchant->config['key'] == $this->request->params['password']) {
326 | $this->response->error(PaycomException::ERROR_INSUFFICIENT_PRIVILEGE, 'Insufficient privilege. Incorrect new password.');
327 | }
328 |
329 | // todo: Implement saving password into data store or file
330 | // example implementation, that saves new password into file specified in the configuration
331 | if (!file_put_contents($this->config['keyFile'], $this->request->params['password'])) {
332 | $this->response->error(PaycomException::ERROR_INTERNAL_SYSTEM, 'Internal System Error.');
333 | }
334 |
335 | // if control is here, then password is saved into data store
336 | // send success response
337 | $this->response->send(['success' => true]);
338 | }
339 |
340 | private function GetStatement()
341 | {
342 | // validate 'from'
343 | if (!isset($this->request->params['from'])) {
344 | $this->response->error(PaycomException::ERROR_INVALID_ACCOUNT, 'Incorrect period.', 'from');
345 | }
346 |
347 | // validate 'to'
348 | if (!isset($this->request->params['to'])) {
349 | $this->response->error(PaycomException::ERROR_INVALID_ACCOUNT, 'Incorrect period.', 'to');
350 | }
351 |
352 | // validate period
353 | if (1 * $this->request->params['from'] >= 1 * $this->request->params['to']) {
354 | $this->response->error(PaycomException::ERROR_INVALID_ACCOUNT, 'Incorrect period. (from >= to)', 'from');
355 | }
356 |
357 | // get list of transactions for specified period
358 | $transaction = new Transaction($this->db_conn);
359 | $transactions = $transaction->report($this->request->params['from'], $this->request->params['to']);
360 |
361 | // send results back
362 | $this->response->send(['transactions' => $transactions]);
363 | }
364 | }
--------------------------------------------------------------------------------
/Paycom/Database.php:
--------------------------------------------------------------------------------
1 | config = $config;
12 | }
13 |
14 | public function new_connection()
15 | {
16 | $db = null;
17 |
18 | // connect to the database
19 | $db_options = [\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC];
20 |
21 | $db = new \PDO(
22 | 'mysql:dbname=' . $this->config['db']['database'] . ';host=localhost;charset=utf8',
23 | $this->config['db']['username'],
24 | $this->config['db']['password'],
25 | $db_options
26 | );
27 |
28 | return $db;
29 | }
30 | }
--------------------------------------------------------------------------------
/Paycom/Format.php:
--------------------------------------------------------------------------------
1 | config = $config;
11 |
12 | // read key from key file
13 | if ($this->config['keyFile']) {
14 | $this->config['key'] = file_get_contents($this->config['keyFile']);
15 | }
16 | }
17 |
18 | public function Authorize($request_id)
19 | {
20 | $headers = getallheaders();
21 |
22 | if (!$headers || !isset($headers['Authorization']) ||
23 | !preg_match('/^\s*Basic\s+(\S+)\s*$/i', $headers['Authorization'], $matches) ||
24 | base64_decode($matches[1]) != $this->config['login'] . ":" . $this->config['key']
25 | ) {
26 | throw new PaycomException(
27 | $request_id,
28 | 'Insufficient privilege to perform this method.',
29 | PaycomException::ERROR_INSUFFICIENT_PRIVILEGE
30 | );
31 | }
32 |
33 | return true;
34 | }
35 | }
--------------------------------------------------------------------------------
/Paycom/Order.php:
--------------------------------------------------------------------------------
1 | request_id = $request_id;
24 | }
25 |
26 | /**
27 | * Validates amount and account values.
28 | * @param array $params amount and account parameters to validate.
29 | * @return bool true - if validation passes
30 | * @throws PaycomException - if validation fails
31 | */
32 | public function validate(array $params)
33 | {
34 | // todo: Validate amount, if failed throw error
35 | // for example, check amount is numeric
36 | if (!is_numeric($params['amount'])) {
37 | throw new PaycomException(
38 | $this->request_id,
39 | 'Incorrect amount.',
40 | PaycomException::ERROR_INVALID_AMOUNT
41 | );
42 | }
43 |
44 | // todo: Validate account, if failed throw error
45 | // assume, we should have order_id
46 | if (!isset($params['account']['order_id'])) {
47 | throw new PaycomException(
48 | $this->request_id,
49 | PaycomException::message(
50 | 'Неверный код заказа.',
51 | 'Harid kodida xatolik.',
52 | 'Incorrect order code.'
53 | ),
54 | PaycomException::ERROR_INVALID_ACCOUNT,
55 | 'order_id'
56 | );
57 | }
58 |
59 | // todo: Check is order available
60 |
61 | // assume, after find() $this will be populated with Order data
62 | $this->find($params['account']['order_id']);
63 |
64 | // for example, order state before payment should be 'waiting pay'
65 | if ($this->state != self::STATE_WAITING_PAY) {
66 | throw new PaycomException(
67 | $this->request_id,
68 | 'Order state is invalid.',
69 | PaycomException::ERROR_COULD_NOT_PERFORM
70 | );
71 | }
72 |
73 | // keep params for further use
74 | $this->params = $params;
75 |
76 | return true;
77 | }
78 |
79 | /**
80 | * Find order by given parameters.
81 | * @param mixed $params parameters.
82 | * @return Order|Order[] found order or array of orders.
83 | */
84 | public function find($params)
85 | {
86 | // todo: Implement searching order(s) by given parameters, populate current instance with data
87 | }
88 |
89 | /**
90 | * Change order's state to specified one.
91 | * @param int $state new state of the order
92 | * @return void
93 | */
94 | public function changeState($state)
95 | {
96 | // todo: Implement changing order state (reserve order after create transaction or free order after cancel)
97 | }
98 |
99 | /**
100 | * Check, whether order can be cancelled or not.
101 | * @return bool true - order is cancellable, otherwise false.
102 | */
103 | public function allowCancel()
104 | {
105 | // todo: Implement order cancelling allowance check
106 | }
107 | }
--------------------------------------------------------------------------------
/Paycom/PaycomException.php:
--------------------------------------------------------------------------------
1 | request_id = $request_id;
30 | $this->message = $message;
31 | $this->code = $code;
32 | $this->data = $data;
33 |
34 | // prepare error data
35 | $this->error = ['code' => $this->code];
36 |
37 | if ($this->message) {
38 | $this->error['message'] = $this->message;
39 | }
40 |
41 | if ($this->data) {
42 | $this->error['data'] = $this->data;
43 | }
44 | }
45 |
46 | public function send()
47 | {
48 | header('Content-Type: application/json; charset=UTF-8');
49 |
50 | // create response
51 | $response['id'] = $this->request_id;
52 | $response['result'] = null;
53 | $response['error'] = $this->error;
54 |
55 | echo json_encode($response);
56 | }
57 |
58 | public static function message($ru, $uz = '', $en = '')
59 | {
60 | return ['ru' => $ru, 'uz' => $uz, 'en' => $en];
61 | }
62 | }
--------------------------------------------------------------------------------
/Paycom/Request.php:
--------------------------------------------------------------------------------
1 | CreateTransaction */
13 | public $method;
14 |
15 | /** @var array request parameters, such as amount, account */
16 | public $params;
17 |
18 | /** @var int amount value in coins */
19 | public $amount;
20 |
21 | /**
22 | * Request constructor.
23 | * Parses request payload and populates properties with values.
24 | */
25 | public function __construct()
26 | {
27 | $request_body = file_get_contents('php://input');
28 | $this->payload = json_decode($request_body, true);
29 |
30 | if (!$this->payload) {
31 | throw new PaycomException(
32 | null,
33 | 'Invalid JSON-RPC object.',
34 | PaycomException::ERROR_INVALID_JSON_RPC_OBJECT
35 | );
36 | }
37 |
38 | // populate request object with data
39 | $this->id = isset($this->payload['id']) ? 1 * $this->payload['id'] : null;
40 | $this->method = isset($this->payload['method']) ? trim($this->payload['method']) : null;
41 | $this->params = isset($this->payload['params']) ? $this->payload['params'] : [];
42 | $this->amount = isset($this->payload['params']['amount']) ? 1 * $this->payload['params']['amount'] : null;
43 |
44 | // add request id into params too
45 | $this->params['request_id'] = $this->id;
46 | }
47 |
48 | /**
49 | * Gets account parameter if such exists, otherwise returns null.
50 | * @param string $param name of the parameter.
51 | * @return mixed|null account parameter value or null if such parameter doesn't exists.
52 | */
53 | public function account($param)
54 | {
55 | return isset($this->params['account'], $this->params['account'][$param]) ? $this->params['account'][$param] : null;
56 | }
57 | }
--------------------------------------------------------------------------------
/Paycom/Response.php:
--------------------------------------------------------------------------------
1 | request = $request;
13 | }
14 |
15 | /**
16 | * Sends response with the given result and error.
17 | * @param mixed $result result of the request.
18 | * @param mixed|null $error error.
19 | */
20 | public function send($result, $error = null)
21 | {
22 | header('Content-Type: application/json; charset=UTF-8');
23 |
24 | $response['id'] = $this->request->id;
25 | $response['result'] = $result;
26 | $response['error'] = $error;
27 |
28 | echo json_encode($response);
29 | }
30 |
31 | /**
32 | * Generates PaycomException exception with given parameters.
33 | * @param int $code error code.
34 | * @param string|array $message error message.
35 | * @param string $data parameter name, that resulted to this error.
36 | * @throws PaycomException
37 | */
38 | public function error($code, $message = null, $data = null)
39 | {
40 | throw new PaycomException($this->request->id, $message, $code, $data);
41 | }
42 | }
--------------------------------------------------------------------------------
/Paycom/Transaction.php:
--------------------------------------------------------------------------------
1 | db_conn = $db_conn;
92 | }
93 |
94 | /**
95 | * Saves current transaction instance in a data store.
96 | * @return void
97 | */
98 | public function save()
99 | {
100 | // todo: Implement creating/updating transaction into data store
101 | // todo: Populate $id property with newly created transaction id
102 | }
103 |
104 | /**
105 | * Cancels transaction with the specified reason.
106 | * @param int $reason cancelling reason.
107 | * @return void
108 | */
109 | public function cancel($reason)
110 | {
111 | // todo: Implement transaction cancelling on data store
112 |
113 | // todo: Populate $cancel_time with value
114 | $this->cancel_time = Format::timestamp2datetime(Format::timestamp());
115 |
116 | // todo: Change $state to cancelled (-1 or -2) according to the current state
117 | // Scenario: CreateTransaction -> CancelTransaction
118 | $this->state = self::STATE_CANCELLED;
119 | // Scenario: CreateTransaction -> PerformTransaction -> CancelTransaction
120 | if ($this->state == self::STATE_COMPLETED) {
121 | $this->state = self::STATE_CANCELLED_AFTER_COMPLETE;
122 | }
123 |
124 | // set reason
125 | $this->reason = $reason;
126 |
127 | // todo: Update transaction on data store
128 | }
129 |
130 | /**
131 | * Determines whether current transaction is expired or not.
132 | * @return bool true - if current instance of the transaction is expired, false - otherwise.
133 | */
134 | public function isExpired()
135 | {
136 | // todo: Implement transaction expiration check
137 | // for example, if transaction is active and passed TIMEOUT milliseconds after its creation, then it is expired
138 | return $this->state == self::STATE_CREATED && Format::datetime2timestamp($this->create_time) - time() > self::TIMEOUT;
139 | }
140 |
141 | /**
142 | * Find transaction by given parameters.
143 | * @param mixed $params parameters
144 | * @return Transaction|Transaction[]
145 | */
146 | public function find($params)
147 | {
148 | $is_success = false;
149 | $db_stmt = null;
150 |
151 | // todo: Implement searching transaction by id, populate current instance with data and return it
152 | if (isset($params['id'])) {
153 | $sql = 'select * from transactions where paycom_transaction_id=:trx_id';
154 | $db_stmt = $this->db_conn->prepare($sql);
155 | $is_success = $db_stmt->execute([':trx_id' => $params['id']]);
156 | }
157 |
158 | // todo: Implement searching transactions by given parameters and return list of transactions
159 |
160 | // if SQL operation succeeded, then try to populate instance properties with values
161 | if ($is_success) {
162 |
163 | // fetch one row
164 | $row = $db_stmt->fetch();
165 |
166 | // if there is row available, then populate properties with values
167 | if ($row) {
168 |
169 | $this->id = $row['id'];
170 | $this->paycom_transaction_id = $row['paycom_transaction_id'];
171 | $this->paycom_time = 1 * $row['paycom_time'];
172 | $this->paycom_time_datetime = $row['paycom_time_datetime'];
173 | $this->create_time = $row['create_time'];
174 | $this->perform_time = $row['perform_time'];
175 | $this->cancel_time = $row['cancel_time'];
176 | $this->state = 1 * $row['state'];
177 | $this->reason = $row['reason'] ? 1 * $row['reason'] : null;
178 | $this->amount = 1 * $row['amount'];
179 | $this->order_id = 1 * $row['order_id'];
180 |
181 | // assume, receivers column contains list of receivers in JSON format as string
182 | $this->receivers = $row['receivers'] ? json_decode($row['receivers'], true) : null;
183 |
184 | // return populated instance
185 | return $this;
186 | }
187 |
188 | }
189 |
190 | // transaction not found, return null
191 | return null;
192 |
193 | // Possible features:
194 | // Search transaction by product/order id that specified in $params
195 | // Search transactions for a given period of time that specified in $params
196 | }
197 |
198 | /**
199 | * Gets list of transactions for the given period including period boundaries.
200 | * @param int $from_date start of the period in timestamp.
201 | * @param int $to_date end of the period in timestamp.
202 | * @return array list of found transactions converted into report format for send as a response.
203 | */
204 | public function report($from_date, $to_date)
205 | {
206 | $from_date = Format::timestamp2datetime($from_date);
207 | $to_date = Format::timestamp2datetime($to_date);
208 |
209 | // container to hold rows/document from data store
210 | $rows = [];
211 |
212 | // todo: Retrieve transactions for the specified period from data store
213 |
214 | // assume, here we have $rows variable that is populated with transactions from data store
215 | // normalize data for response
216 | $result = [];
217 | foreach ($rows as $row) {
218 | $result[] = [
219 | 'id' => $row['paycom_transaction_id'], // paycom transaction id
220 | 'time' => 1 * $row['paycom_time'], // paycom transaction timestamp as is
221 | 'amount' => 1 * $row['amount'],
222 | 'account' => [
223 | 'order_id' => $row['order_id'], // account parameters to identify client/order/service
224 | // ... additional parameters may be listed here, which are belongs to the account
225 | ],
226 | 'create_time' => Format::datetime2timestamp($row['create_time']),
227 | 'perform_time' => Format::datetime2timestamp($row['perform_time']),
228 | 'cancel_time' => Format::datetime2timestamp($row['cancel_time']),
229 | 'transaction' => $row['id'],
230 | 'state' => 1 * $row['state'],
231 | 'reason' => isset($row['reason']) ? 1 * $row['reason'] : null,
232 | 'receivers' => $row['receivers']
233 | ];
234 | }
235 |
236 | return $result;
237 | }
238 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NOTE
2 |
3 | Please, use the following up to date repo instead https://github.com/PaycomUZ/paycom-integration-php-template
4 |
5 | # Paycom integration template
6 |
7 | ## Prerequisites
8 |
9 | - `PHP 5.4` or greater
10 | - [`PDO`](http://php.net/manual/en/book.pdo.php) extension
11 | - [`Composer`](https://getcomposer.org/download/) dependency manager
12 |
13 | ## Installation via Composer
14 |
15 | Change current directory to your project folder and install package:
16 | ```
17 | cd my-shop-project
18 | composer create-project paycom/integration-template
19 | ```
20 |
21 | ## Installation via Git
22 | ```
23 | git clone https://github.com/umidjons/paycom-integration-php-template.git
24 | cd paycom-integration-php-template
25 | composer dumpautoload
26 | ```
27 |
28 | From now you can use classes from package as following:
29 | ```php
30 | run();
41 | ```
42 |
43 | Make copy of `paycom.config.sample.php` as `paycom.config.php` and set your real settings there.
44 |
45 | Assuming your domain as `https://myshop.uz`,
46 | now you can set entry point URL to handle requests from Paycom as `https://myshop.uz/api/index.php`.
47 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "paycom/integration-template",
3 | "description": "Set of classes to easy integration with Paycom (Payme) payment system",
4 | "type": "library",
5 | "keywords": [
6 | "paycom",
7 | "payme"
8 | ],
9 | "authors": [
10 | {
11 | "name": "Umidjons",
12 | "email": "almatov.us@gmail.com"
13 | }
14 | ],
15 | "autoload": {
16 | "psr-4": {
17 | "Paycom\\": "Paycom/"
18 | }
19 | },
20 | "require": {
21 | "php": ">=5.4.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 | $value) {
9 | if (substr($name, 0, 5) == 'HTTP_') {
10 | $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
11 | }
12 | }
13 | return $headers;
14 | }
15 | }
--------------------------------------------------------------------------------
/index.php:
--------------------------------------------------------------------------------
1 | run();
16 |
--------------------------------------------------------------------------------
/password.paycom:
--------------------------------------------------------------------------------
1 | Replace this line with your KEY, that is obtained from the merchant's cabinet
--------------------------------------------------------------------------------
/paycom.config.sample.php:
--------------------------------------------------------------------------------
1 | '69240ea9058e46ea7a1b806a',
5 | 'login' => 'Paycom',
6 | 'keyFile' => 'password.paycom',
7 | 'db' => [
8 | 'database' => '',
9 | 'username' => '',
10 | 'password' => ''
11 | ],
12 | ];
--------------------------------------------------------------------------------