Completed
Push — master ( d0a149...d07a9e )
by Arthur
03:35
created

PaymentRepository::markPaymentPending()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php namespace BB\Repo;
2
3
use BB\Entities\Payment;
4
use BB\Events\MemberBalanceChanged;
5
use BB\Exceptions\NotImplementedException;
6
use BB\Exceptions\PaymentException;
7
use Carbon\Carbon;
8
9
class PaymentRepository extends DBRepository
10
{
11
12
    /**
13
     * @var Payment
14
     */
15
    protected $model;
16
17
    public static $SUBSCRIPTION = 'subscription';
18
    public static $INDUCTION = 'induction';
19
20
    private $reason = null;
21
    private $source = null;
22
23
    /**
24
     * @param Payment $model
25
     */
26
    public function __construct(Payment $model)
27
    {
28
        $this->model = $model;
29
        $this->perPage = 10;
30
    }
31
32
33
    public function getPaginated(array $params)
34
    {
35
        $model = $this->model;
36
37 View Code Duplication
        if ($this->hasDateFilter()) {
38
            $model = $model->where('created_at', '>=', $this->startDate)->where('created_at', '<=', $this->endDate);
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
39
        }
40
41
        if ($this->hasMemberFilter()) {
42
            $model = $model->where('user_id', $this->memberId);
43
        }
44
45
        if ($this->hasReasonFilter()) {
46
            $model = $model->where('reason', $this->reason);
47
        }
48
49
        if ($this->hasSourceFilter()) {
50
            $model = $model->where('source', $this->source);
51
        }
52
53 View Code Duplication
        if ($this->isSortable($params)) {
54
            return $model->orderBy($params['sortBy'], $params['direction'])->paginate($this->perPage);
55
        }
56
        return $model->paginate($this->perPage);
57
    }
58
59
60
    public function getTotalAmount()
61
    {
62
        $model = $this->model;
63
64 View Code Duplication
        if ($this->hasDateFilter()) {
65
            $model = $model->where('created_at', '>=', $this->startDate)->where('created_at', '<=', $this->endDate);
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
66
        }
67
68
        if ($this->hasMemberFilter()) {
69
            $model = $model->where('user_id', $this->memberId);
70
        }
71
72
        if ($this->hasReasonFilter()) {
73
            $model = $model->where('reason', $this->reason);
74
        }
75
76
        if ($this->hasSourceFilter()) {
77
            $model = $model->where('source', $this->source);
78
        }
79
80
        return $model->get()->sum('amount');
81
    }
82
83
84
    /**
85
     * Record a payment against a user record
86
     *
87
     * @param string   $reason What was the reason. subscription, induction, etc...
88
     * @param int      $userId The users ID
89
     * @param string   $source gocardless, paypal
90
     * @param string   $sourceId A reference for the source
91
     * @param double   $amount Amount received before a fee in pounds
92
     * @param string   $status paid, pending, cancelled, refunded
93
     * @param double   $fee The fee charged by the payment provider
94
     * @param string   $ref
95
     * @param Carbon $paidDate
96
     * @return int The ID of the payment record
97
     */
98
    public function recordPayment($reason, $userId, $source, $sourceId, $amount, $status = 'paid', $fee = 0.0, $ref = '', Carbon $paidDate = null)
99
    {
100
        if ($paidDate == null) {
101
            $paidDate = new Carbon();
102
        }
103
        //If we have an existing similer record dont create another, except for when there is no source id
104
        $existingRecord = $this->model->where('source', $source)->where('source_id', $sourceId)->where('user_id', $userId)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
105
        if ( ! $existingRecord || empty($sourceId)) {
106
            $record                   = new $this->model;
107
            $record->user_id          = $userId;
108
            $record->reason           = $reason;
109
            $record->source           = $source;
110
            $record->source_id        = $sourceId;
111
            $record->amount           = $amount;
112
            $record->amount_minus_fee = ($amount - $fee);
113
            $record->fee              = $fee;
114
            $record->status           = $status;
115
            $record->reference        = $ref;
116
            if ($status == 'paid') {
117
                $record->paid_at = $paidDate;
118
            }
119
            $record->save();
120
        } else {
121
            $record = $existingRecord;
122
        }
123
124
        //Emit an event so that things like the balance updater can run
125
        \Event::fire('payment.create', array($userId, $reason, $ref, $record->id, $status));
126
127
        return $record->id;
128
    }
129
130
    /**
131
     * Record a subscription payment
132
     *
133
     * @param int    $userId The users ID
134
     * @param string $source gocardless, paypal
135
     * @param string $sourceId A reference for the source
136
     * @param double $amount Amount received before a fee in pounds
137
     * @param string $status paid, pending, cancelled, refunded
138
     * @param double $fee The fee charged by the payment provider
139
     * @param string|null   $ref
140
     * @param Carbon $paidDate
141
     * @return int  The ID of the payment record
142
     */
143
    public function recordSubscriptionPayment($userId, $source, $sourceId, $amount, $status = 'paid', $fee = 0.0, $ref = '', Carbon $paidDate = null)
144
    {
145
        return $this->recordPayment('subscription', $userId, $source, $sourceId, $amount, $status, $fee, $ref, $paidDate);
146
    }
147
148
    /**
149
     * An existing payment has been set to paid
150
     *
151
     * @param $paymentId
152
     * @param Carbon $paidDate
153
     */
154
    public function markPaymentPaid($paymentId, $paidDate)
155
    {
156
        $payment = $this->getById($paymentId);
157
        $payment->status = 'paid';
158
        $payment->paid_at = $paidDate;
159
        $payment->save();
160
161
        \Event::fire('payment.paid', array($payment->user_id, $paymentId, $payment->reason, $payment->reference, $paidDate));
162
    }
163
164
    /**
165
     * An existing payment has been set to pending/submitted
166
     *
167
     * @param $paymentId
168
     */
169
    public function markPaymentPending($paymentId)
170
    {
171
        $payment = $this->getById($paymentId);
172
        $payment->status = 'pending';
173
        $payment->save();
174
    }
175
176
    /**
177
     * Record a payment failure or cancellation
178
     *
179
     * @param int    $paymentId
180
     * @param string $status
181
     */
182
    public function recordPaymentFailure($paymentId, $status = 'failed')
183
    {
184
        $this->update($paymentId, ['status' => $status]);
185
186
        $payment = $this->getById($paymentId);
187
188
        \Event::fire('payment.cancelled', array($paymentId, $payment->user_id, $payment->reason, $payment->reference, $status));
189
    }
190
191
    /**
192
     * Assign an unassigned payment to a user
193
     *
194
     * @param int $paymentId
195
     * @param int $userId
196
     *
197
     * @throws PaymentException
198
     */
199
    public function assignPaymentToUser($paymentId, $userId)
200
    {
201
        $payment = $this->getById($paymentId);
202
203
        if (!empty($payment->user_id)) {
204
            throw new PaymentException('Payment already assigned to user');
205
        }
206
207
        $this->update($paymentId, ['user_id' => $userId]);
208
    }
209
210
    /**
211
     * Take a payment that has been used for something and reassign it to the balance
212
     * @param $paymentId
213
     *
214
     * @throws NotImplementedException
215
     */
216
    public function refundPaymentToBalance($paymentId)
217
    {
218
        $payment = $this->getById($paymentId);
219
220 View Code Duplication
        if ($payment->reason === 'donation') {
221
            $this->update($paymentId, ['reason' => 'balance']);
222
            event(new MemberBalanceChanged($payment->user_id));
223
            return;
224
        }
225
226 View Code Duplication
        if ($payment->reason === 'induction') {
227
            //This method must only be used if the induction record has been cancelled first
228
            // otherwise an orphned record will be left behind
229
            $this->update($paymentId, ['reason' => 'balance']);
230
            event(new MemberBalanceChanged($payment->user_id));
231
            return;
232
        }
233
234
        throw new NotImplementedException('This hasn\'t been built yet');
235
    }
236
237
238
    /**
239
     * Fetch the users latest payment of a particular type
240
     * @param integer $userId
241
     * @param string  $reason
242
     * @return mixed
243
     */
244 View Code Duplication
    public function latestUserPayment($userId, $reason = 'subscription')
245
    {
246
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
247
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', [$reason, 'paid', 'pending', 'withdrawn'])
248
            ->orderBy('created_at', 'desc')
249
            ->first();
250
    }
251
252
253
    /**
254
     * Get all user payments of a specific reason
255
     * @param $userId
256
     * @param string $reason
257
     * @return mixed
258
     */
259 View Code Duplication
    public function getUserPaymentsByReason($userId, $reason)
260
    {
261
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
262
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', [$reason, 'paid', 'pending', 'withdrawn'])
263
            ->orderBy('created_at', 'desc')
264
            ->get();
265
    }
266
267
268
    /**
269
     * @param string $source
270
     */
271 View Code Duplication
    public function getUserPaymentsBySource($userId, $source)
272
    {
273
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
274
            ->whereRaw('source = ? and (status = ? or status = ? or status = ?)', [$source, 'paid', 'pending', 'withdrawn'])
275
            ->orderBy('created_at', 'desc')
276
            ->get();
277
    }
278
279
    /**
280
     * Get all payments with a specific reference
281
     * @param string $reference
282
     * @return \Illuminate\Database\Eloquent\Collection
283
     */
284
    public function getPaymentsByReference($reference)
285
    {
286
        return $this->model->where('reference', $reference)->get();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
287
    }
288
289
    /**
290
     * @param string $referencePrefix
291
     * @return \Illuminate\Database\Eloquent\Collection
292
     */
293
    public function getEquipmentFeePayments($referencePrefix)
294
    {
295
        return $this->model->where('reason', 'equipment-fee')->get()->filter(function($payment) use($referencePrefix) {
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
296
            return strpos($payment->reference, ':' . $referencePrefix) !== false;
297
        });
298
    }
299
300
301
    /**
302
     * Return a paginated list of balance affecting payment for a user
303
     * @param $userId
304
     * @return \Illuminate\Database\Eloquent\Collection
305
     */
306 View Code Duplication
    public function getBalancePaymentsPaginated($userId)
307
    {
308
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
309
            ->whereRaw('(source = ? or reason = ?) and (status = ? or status = ? or status = ?)', ['balance', 'balance', 'paid', 'pending', 'withdrawn'])
310
            ->orderBy('created_at', 'desc')
311
            ->simplePaginate($this->perPage);
312
    }
313
314
315
    /**
316
     * Return a collection of payments specifically for storage boxes
317
     * @param integer $userId
318
     * @return mixed
319
     */
320 View Code Duplication
    public function getStorageBoxPayments($userId)
321
    {
322
        return $this->model->where('user_id', $userId)
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
323
            ->whereRaw('reason = ? and (status = ? or status = ? or status = ?)', ['storage-box', 'paid', 'pending', 'withdrawn'])
324
            ->orderBy('created_at', 'desc')
325
            ->get();
326
    }
327
328
    public function dateFilter($startDate, $endDate)
329
    {
330
        $this->startDate = $startDate;
331
        $this->endDate = $endDate;
332
    }
333
334
    private function hasDateFilter()
335
    {
336
        return ($this->startDate && $this->endDate);
337
    }
338
339
    /**
340
     * Delete a record
341
     * @param $recordId
342
     * @return bool|null
343
     * @throws \Exception
344
     */
345
    public function delete($recordId)
346
    {
347
        $payment = $this->getById($recordId);
348
349
        $state = $payment->delete();
350
351
        //Fire an event, allows the balance to get updated
352
        \Event::fire('payment.delete', array($payment->user_id, $payment->source, $payment->reason, $payment->id));
353
354
        return $state;
355
    }
356
357
    public function canDelete($recordId)
358
    {
359
        throw new NotImplementedException();
360
    }
361
362
    /**
363
     * Used for the getPaginated and getTotalAmount method
364
     * @param $reasonFilter
365
     */
366
    public function reasonFilter($reasonFilter)
367
    {
368
        $this->reason = $reasonFilter;
369
    }
370
371
    private function hasReasonFilter()
372
    {
373
        return ! is_null($this->reason);
374
    }
375
376
    /**
377
     * Used for the getPaginated and getTotalAmount method
378
     * @param string $sourceFilter
379
     */
380
    public function sourceFilter($sourceFilter)
381
    {
382
        $this->source = $sourceFilter;
383
    }
384
385
386
    private function hasSourceFilter()
387
    {
388
        return ! is_null($this->source);
389
    }
390
391
    /**
392
     * Used for the getPaginated and getTotalAmount method
393
     */
394
    public function resetFilters()
395
    {
396
        $this->source = null;
397
        $this->reason = null;
398
        $this->memberId = null;
399
        $this->startDate = null;
400
        $this->endDate = null;
401
    }
402
403
    /**
404
     * Fetch a payment record using the id provided by the payment provider
405
     *
406
     * @param $sourceId
407
     * @return Payment
408
     */
409
    public function getPaymentBySourceId($sourceId)
410
    {
411
        return $this->model->where('source_id', $sourceId)->first();
0 ignored issues
show
Documentation Bug introduced by
The method where does not exist on object<BB\Entities\Payment>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
412
    }
413
414
    /**
415
     * Record a balance payment transfer between two users
416
     * 
417
     * @param integer $sourceUserId
418
     * @param integer $targetUserId
419
     * @param double $amount
420
     */
421
    public function recordBalanceTransfer($sourceUserId, $targetUserId, $amount)
422
    {
423
        $paymentId = $this->recordPayment('transfer', $sourceUserId, 'balance', '', $amount, 'paid', 0, $targetUserId);
424
        $this->recordPayment('balance', $targetUserId, 'transfer', $paymentId, $amount, 'paid', 0, $sourceUserId);
425
426
        //Both of these events aren't needed adn the balance payment fires its own
427
        // but for the sake of neatness they are here
428
        event(new MemberBalanceChanged($sourceUserId));
429
        event(new MemberBalanceChanged($targetUserId));
430
    }
431
} 
432