Issues (19)

src/Models/Recurring.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Greenlyst\BaseCommerce\Models;
6
7
use function ArrayHelpers\array_get;
8
use Carbon\Carbon;
9
use Carbon\CarbonInterface;
10
use Greenlyst\BaseCommerce\ClientException;
11
use Greenlyst\BaseCommerce\Core\Helpers;
12
use Greenlyst\BaseCommerce\LogicException;
13
use Greenlyst\BaseCommerce\Traits\HasClient;
14
use Greenlyst\BaseCommerce\Traits\HasCustomFields;
15
use Greenlyst\BaseCommerce\Traits\HasErrorMessages;
16
17
final class Recurring
18
{
19
    use HasErrorMessages, HasCustomFields, HasClient;
20
21
    private $frequency;
22
23
    /**
24
     * @var Carbon
25
     */
26
    private $startDate;
27
    /**
28
     * @var Carbon
29
     */
30
    private $endDate;
31
    private $status;
32
    private $transactionId;
33
    private $card;
34
    private $amount;
35
36
    const FREQUENCY_ANNUALLY = 'ANNUALLY';
37
    const FREQUENCY_QUARTERLY = 'QUARTERLY';
38
    const FREQUENCY_MONTHLY = 'MONTHLY';
39
    const FREQUENCY_BIWEEKLY = 'BIWEEKLY';
40
    const FREQUENCY_WEEKLY = 'WEEKLY';
41
    const FREQUENCY_DAILY = 'DAILY';
42
43
    const RECURRING_STATUS_ENABLED = 'RECURRINGENABLED';
44
    const RECURRING_STATUS_FAILED = 'RECURRINGFAILED';
45
    const RECURRING_STATUS_DISABLED = 'RECURRINGDISABLED';
46
    const RECURRING_STATUS_COMPLETED = 'RECURRINGCOMPLETED';
47
48
    const CUSTOM_FIELD_PREFIX = 'recurring_transaction';
49
50
    const URI_CREATE_RECURRING_TRANSACTION = '/pcms/?f=API_processRecurringTransaction';
51
    const URI_CANCEL_RECURRING_TRANSACTION = '/pcms/?f=API_cancelRecurringTransaction';
52
53
    const TRANSACTION_TYPE_SALE = 'SALE';
54
55
    /**
56
     * @return mixed
57
     */
58
    public function getFrequency()
59
    {
60
        return $this->frequency;
61
    }
62
63
    /**
64
     * @param mixed $frequency
65
     */
66
    public function setFrequency($frequency): void
67
    {
68
        $this->frequency = $frequency;
69
    }
70
71
    /**
72
     * @return string
73
     */
74
    public function getStartDate()
75
    {
76
        if ($this->startDate == null) {
77
            return Carbon::now()->addDay()->format('m/d/Y');
78
        }
79
80
        return $this->startDate->addDay()->format('m/d/Y');
81
    }
82
83
    /**
84
     * @param CarbonInterface $startDate
85
     */
86
    public function setStartDate(CarbonInterface $startDate): void
87
    {
88
        $this->startDate = $startDate;
0 ignored issues
show
Documentation Bug introduced by
$startDate is of type Carbon\CarbonInterface, but the property $startDate was declared to be of type Carbon\Carbon. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
89
    }
90
91
    /**
92
     * @return CarbonInterface
93
     */
94
    public function getEndDate()
95
    {
96
        return $this->endDate;
97
    }
98
99
    /**
100
     * @param CarbonInterface $endDate
101
     */
102
    public function setEndDate(CarbonInterface $endDate): void
103
    {
104
        $this->endDate = $endDate;
0 ignored issues
show
Documentation Bug introduced by
$endDate is of type Carbon\CarbonInterface, but the property $endDate was declared to be of type Carbon\Carbon. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
105
    }
106
107
    /**
108
     * @return mixed
109
     */
110
    public function getStatus()
111
    {
112
        return $this->status;
113
    }
114
115
    /**
116
     * @param mixed $status
117
     */
118
    public function setStatus($status): void
119
    {
120
        $this->status = $status;
121
    }
122
123
    /**
124
     * @return mixed
125
     */
126
    public function getTransactionId()
127
    {
128
        return $this->transactionId;
129
    }
130
131
    /**
132
     * @param mixed $transactionId
133
     */
134
    public function setTransactionId($transactionId): void
135
    {
136
        $this->transactionId = $transactionId;
137
    }
138
139
    /**
140
     * @return Card
141
     */
142
    public function getCard()
143
    {
144
        return $this->card;
145
    }
146
147
    /**
148
     * @param Card $card
149
     */
150
    public function setCard($card): void
151
    {
152
        $this->card = $card;
153
    }
154
155
    /**
156
     * @return mixed
157
     */
158
    public function getAmount()
159
    {
160
        return $this->amount;
161
    }
162
163
    /**
164
     * @param mixed $amount
165
     */
166
    public function setAmount($amount): void
167
    {
168
        $this->amount = $amount;
169
    }
170
171
    protected function getCustomFieldPrefix(): string
172
    {
173
        return self::CUSTOM_FIELD_PREFIX;
174
    }
175
176
    private function isVaultTransaction()
177
    {
178
        if ($this->getCard() !== null) {
179
            return !empty($this->getCard()->getToken());
180
        }
181
182
        //TODO: Do the same for ACH Bank Account Token as well
183
        return false;
184
    }
185
186
    private function isCardTransaction()
187
    {
188
        return $this->getCard() !== null;
189
    }
190
191
    /**
192
     * @throws LogicException
193
     * @throws ClientException
194
     */
195
    public function createRecurringSale()
196
    {
197
        if ($this->isCardTransaction()) {
198
            if ($this->isVaultTransaction()) {
199
                validate_array($this->toCreateRecurringArray(), [
200
                    'bank_card', 'recurring_transaction_frequency', 'recurring_transaction_start_date',
201
                    'bank_card.bank_card_token',
202
                ]);
203
            }
204
        } else {
205
            throw LogicException::pendingImplementation('ACH Bank Transaction implementation');
206
        }
207
208
        $response = $this->client->postRequest(self::URI_CREATE_RECURRING_TRANSACTION, json_encode($this->toCreateRecurringArray()));
209
210
        $instance = $this->fromRecurringArray($response['recurring_transaction']);
211
212
        $instance->handleMessages($response);
213
214
        return $instance;
215
    }
216
217
    /**
218
     * @throws LogicException
219
     * @throws ClientException
220
     */
221
    public function cancelRecurringSale()
222
    {
223
        Helpers::validateArray($this->toCancelRecurringArray(), ['recurring_transaction_id']);
224
225
        $response = $this->client->postRequest(self::URI_CANCEL_RECURRING_TRANSACTION, array_get($this->toCancelRecurringArray(), 'recurring_transaction_id'));
226
227
        $instance = $this->fromRecurringArray($response['recurring_transaction']);
228
229
        $instance->handleMessages($response);
230
231
        return $instance;
232
    }
233
234
    private function fromRecurringArray(array $data)
235
    {
236
        $instance = new static();
237
238
        $instance->setFrequency(array_get($data, 'recurring_transaction_frequency'));
239
        $instance->setStartDate(Carbon::parse(array_get($data, 'recurring_transaction_start_date')));
240
        $instance->setEndDate(Carbon::parse(array_get($data, 'recurring_transaction_end_date')));
241
        $instance->setStatus(array_get($data, 'recurring_transaction_status.transaction_status_name'));
242
        $instance->setTransactionId(array_get($data, 'recurring_transaction_id'));
243
244
        return $instance;
245
    }
246
247
    private function toCreateRecurringArray()
248
    {
249
        return clear_array(array_merge($this->getCustomFields(), [
250
            'recurring_transaction_frequency'  => $this->getFrequency(),
251
            'recurring_transaction_start_date' => $this->getStartDate(),
252
            'recurring_transaction_end_date'   => $this->getEndDate() ? $this->getEndDate() : null,
253
            'recurring_transaction_amount'     => $this->getAmount(),
254
            'bank_card'                        => $this->getCard()->toCreateCardArray(),
255
            'recurring_transaction_type'       => self::TRANSACTION_TYPE_SALE,
256
        ]));
257
    }
258
259
    private function toCancelRecurringArray()
260
    {
261
        return clear_array([
262
            'recurring_transaction_id' => $this->getTransactionId(),
263
        ]);
264
    }
265
}
266