Refund::create()   A
last analyzed

Complexity

Conditions 2
Paths 4

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 9.504
c 0
b 0
f 0
cc 2
nc 4
nop 2
1
<?php
2
namespace CultureKings\Afterpay\Service\InStore;
3
4
use CultureKings\Afterpay\Exception\ApiException;
5
use CultureKings\Afterpay\Model;
6
use GuzzleHttp\Exception\BadResponseException;
7
use GuzzleHttp\Exception\RequestException;
8
use GuzzleHttp\HandlerStack;
9
10
/**
11
 * Class Refund
12
 * @package CultureKings\Afterpay\Service\InStore
13
 */
14
class Refund extends AbstractService
15
{
16
    const ERROR_CONFLICT = 409;
17
    const ERROR_INVALID_ORDER_MERCHANT_REFERENCE = 412;
18
    const ERROR_PRECONDITION_FAILED = 412;
19
    const ERROR_INVALID_AMOUNT = 412;
20
    const ERROR_INTERNAL_ERROR = 500;
21
    const ERROR_MSG_PRECONDITION_FAILED = 'precondition_failed';
22
23
    /**
24
     * @param Model\InStore\Refund $refund
25
     * @param HandlerStack|null    $stack
26
     *
27
     * @return Model\InStore\Refund|array|\JMS\Serializer\scalar|object
28
     */
29
    public function create(Model\InStore\Refund $refund, HandlerStack $stack = null)
30
    {
31
        try {
32
            $params = $this->generateParams(
33
                $refund,
34
                $stack
35
            );
36
37
            $result = $this->getClient()->post('refunds', $params);
38
39
            return $this->getSerializer()->deserialize(
40
                (string) $result->getBody(),
41
                Model\InStore\Refund::class,
42
                'json'
43
            );
44
        } catch (BadResponseException $exception) {
45
            throw new ApiException(
46
                $this->getSerializer()->deserialize(
47
                    (string) $exception->getResponse()->getBody(),
48
                    Model\ErrorResponse::class,
49
                    'json'
50
                ),
51
                $exception
52
            );
53
        }
54
    }
55
56
    /**
57
     * @param Model\InStore\Reversal $reversal
58
     * @param HandlerStack|null      $stack
59
     *
60
     * @return array|\JMS\Serializer\scalar|Model\InStore\Reversal
61
     */
62
    public function reverse(Model\InStore\Reversal $reversal, HandlerStack $stack = null)
63
    {
64
        try {
65
            $params = $this->generateParams(
66
                $reversal,
67
                $stack
68
            );
69
70
            $result = $this->getClient()->post('refunds/reverse', $params);
71
72
            return $this->getSerializer()->deserialize(
73
                (string) $result->getBody(),
74
                Model\InStore\Reversal::class,
75
                'json'
76
            );
77
        } catch (RequestException $e) {
78
            throw new ApiException(
79
                $this->getSerializer()->deserialize(
80
                    (string) $e->getResponse()->getBody(),
81
                    Model\ErrorResponse::class,
82
                    'json'
83
                ),
84
                $e
85
            );
86
        }
87
    }
88
89
    /**
90
     * Helper method to automatically attempt to reverse a refund if an error occurs.
91
     *
92
     * Refund reversal model does not have to be passed in and will be automatically generated if not.
93
     *
94
     * @param Model\InStore\Refund        $refund
95
     * @param Model\InStore\Reversal|null $refundReversal
96
     * @param HandlerStack|null           $stack
97
     *
98
     * @return Model\InStore\Refund|Model\InStore\Reversal|array|\JMS\Serializer\scalar|object
99
     */
100
    public function createOrReverse(
101
        Model\InStore\Refund $refund,
102
        Model\InStore\Reversal $refundReversal = null,
103
        HandlerStack $stack = null
104
    ) {
105
        try {
106
            return $this->create($refund, $stack);
107
        } catch (ApiException $refundException) {
108
            // http://docs.afterpay.com.au/instore-api-v1.html#create-refund
109
            // Should a success or error response (with exception to 409 conflict) not be received,
110
            // the POS should queue the request ID for reversal
111
            if ($refundException->getErrorResponse()->getErrorCode() == self::ERROR_CONFLICT) {
112
                throw $refundException;
113
            }
114
            $errorResponse = $refundException->getErrorResponse();
115
        } catch (RequestException $refundException) {
116
            // a timeout or other exception has occurred. attempt a reversal
117
            $errorResponse = new Model\ErrorResponse();
118
            $errorResponse->setHttpStatusCode(
119
                $refundException->getResponse() ? $refundException->getResponse()->getStatusCode() : self::ERROR_INTERNAL_ERROR
120
            );
121
            $errorResponse->setMessage($refundException->getMessage());
122
        }
123
124
        $now = new \DateTime();
125
        if ($refundReversal === null) {
126
            $refundReversal = new Model\InStore\Reversal();
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $refundReversal. This often makes code more readable.
Loading history...
127
            $refundReversal->setReversingRequestId($refund->getRequestId());
128
            $refundReversal->setRequestedAt($now);
129
        }
130
131
        try {
132
            $reversal = $this->reverse($refundReversal, $stack);
133
            $reversal->setErrorReason($errorResponse);
134
135
            return $reversal;
136
        } catch (ApiException $reversalException) {
137
            $reversalErrorResponse = $reversalException->getErrorResponse();
138
            if ($reversalErrorResponse->getErrorCode() === self::ERROR_MSG_PRECONDITION_FAILED) {
139
                // there was trouble reversing probably because the refund failed
140
                throw $refundException;
141
            }
142
143
            throw $reversalException;
144
        }
145
    }
146
}
147