PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.3
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.3
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Models / OrderTransaction.php

OrderTransaction.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.3, at app/Models/OrderTransaction.php

276 lines 7.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Models;
4
5 use FluentCart\Api\StoreSettings;
6 use FluentCart\App\Helpers\Status;
7 use FluentCart\App\Models\Concerns\CanSearch;
8 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
9 use FluentCart\Framework\Database\Orm\Relations\HasOne;
10 use FluentCart\Framework\Support\Arr;
11
12 /**
13 * OrderTransaction Model - DB Model for Transactions
14 *
15 * Database Model
16 *
17 * @package FluentCart\App\Models
18 *
19 * @version 1.0.0
20 */
21 class OrderTransaction extends Model
22 {
23 use CanSearch;
24
25 protected $table = 'fct_order_transactions';
26
27 protected $appends = ['url', 'refundable'];
28 /**
29 * The attributes that are mass assignable.
30 *
31 * @var array
32 */
33 protected $fillable = [
34 'order_id',
35 'order_type',
36 'vendor_charge_id',
37 'payment_method',
38 'payment_mode',
39 'payment_method_type',
40 'currency',
41 'transaction_type',
42 'subscription_id',
43 'card_last_4',
44 'card_brand',
45 'status',
46 'total',
47 'rate',
48 'meta',
49 'uuid',
50 'created_at'
51 ];
52
53 protected $searchable = [
54 'id',
55 'total',
56 'status',
57 'payment_method',
58 'currency',
59 'created_at',
60 'updated_at',
61 ];
62
63 public function setMetaAttribute($value)
64 {
65
66 if ($value) {
67 $decoded = \json_encode($value, true);
68 if (!($decoded)) {
69 $decoded = '[]';
70 }
71 } else {
72 $decoded = '[]';
73 }
74
75 $this->attributes['meta'] = $decoded;
76 }
77
78 public function getMetaAttribute($value)
79 {
80 if (!$value) {
81 return [];
82 }
83
84 return \json_decode($value, true);
85 }
86
87 public function subscription()
88 {
89 return $this->hasOne(Subscription::class, 'id', 'subscription_id');
90 }
91
92 public function order()
93 {
94 return $this->belongsTo(Order::class, 'order_id', 'id');
95 }
96
97
98 public static function boot()
99 {
100 parent::boot();
101 static::creating(function ($model) {
102 if (empty($model->uuid)) {
103 $model->uuid = md5(time() . wp_generate_uuid4());
104 }
105 });
106 }
107
108 public function getUrlAttribute($value)
109 {
110
111 return apply_filters('fluent_cart/transaction/url_' . $this->getAttribute('payment_method'), '', [
112 'transaction' => $this,
113 'payment_mode' => $this->payment_mode,
114 'vendor_charge_id' => $this->vendor_charge_id,
115 'transaction_type' => $this->transaction_type
116 ]);
117 }
118
119 public function scopeOfStatus($query, $status)
120 {
121 return $query->where('status', $status);
122 }
123
124 public function scopeOfPaymentMethod($query, $methodName)
125 {
126 return $query->where('payment_method', $methodName);
127 }
128
129 public function updateStatus($newStatus, $otherData = [])
130 {
131 $oldStatus = $this->status;
132
133 if ($newStatus == $oldStatus) {
134 return $this;
135 }
136
137 $this->status = $newStatus;
138
139 if ($otherData) {
140 $this->fill($otherData);
141 }
142
143 $this->save();
144
145 return $this;
146 }
147
148 public static function bulkDeleteByOrderIds($ids, $params = [])
149 {
150 return static::getQuery()->whereIn('order_id', $ids)->delete();
151 }
152
153 public function orders(): HasOne
154 {
155 return $this->hasOne(Order::class, 'id', 'order_id');
156 }
157
158 public function getRefundableAttribute(): int
159 {
160 return $this->getMaxRefundableAmount();
161 }
162
163 public function getMaxRefundableAmount()
164 {
165 if ($this->status !== Status::TRANSACTION_SUCCEEDED) {
166 return 0;
167 }
168 $refundAmount = (int)(Arr::get($this->meta, 'refunded_total', 0));
169 $baseAmount = $this->total - $refundAmount;
170 $filtered = apply_filters('fluent_cart/transaction/max_refundable_amount', $baseAmount, $this);
171 return max(0, min((int) $filtered, $baseAmount));
172 }
173
174 public function getPaymentMethodText()
175 {
176 if ($this->card_brand && $this->card_last_4) {
177 return sprintf('%1$s ***%2$s', esc_html($this->card_brand), esc_html($this->card_last_4));
178 }
179
180 return $this->payment_method;
181 }
182
183 public function getReceiptPageUrl($filtered = false)
184 {
185 $url = add_query_arg([
186 'trx_hash' => $this->uuid
187 ], (new StoreSettings())->getReceiptPage());
188
189 if ($filtered) {
190 $context = [
191 'transaction' => $this,
192 'order' => $this->order,
193 ];
194 $url = apply_filters_deprecated('fluentcart/transaction/receipt_page_url', [$url, $context], '1.3.16', 'fluent_cart/transaction/receipt_page_url', 'Use fluent_cart/transaction/receipt_page_url instead of fluentcart/transaction/receipt_page_url. It will be removed in v1.4.3.');
195 $url = apply_filters('fluent_cart/transaction/receipt_page_url', $url, $context);
196 }
197
198 return $url;
199 }
200
201 public function acceptDispute($args = [])
202 {
203 if ($this->transaction_type !== Status::TRANSACTION_TYPE_DISPUTE) {
204 return new \WP_Error('No dispute found!', __('The selected transaction is not a dispute', 'fluent-cart'));
205 }
206
207 $gateway = GatewayManager::getInstance($this->payment_method);
208
209 if ($gateway && $gateway->has('dispute_handler')) {
210 $handleRemoteDispute = $gateway->acceptRemoteDispute($this, $args);
211 if (is_wp_error($handleRemoteDispute)) {
212 return $handleRemoteDispute;
213 }
214
215 $this->status = Status::TRANSACTION_DISPUTE_LOST;
216 $this->meta = array_merge($this->meta, [
217 'is_dispute_actionable' => false,
218 'is_charge_refundable' => false
219 ]);
220 $this->save();
221
222 $newPaidAmount = intval($this->order->total_paid - $this->total);
223 $this->order->update([
224 'total_paid' => max($newPaidAmount, 0),
225 'payment_status' => $newPaidAmount > 0 ? Status::PAYMENT_PARTIALLY_PAID : Status::PAYMENT_FAILED,
226 ]);
227
228 if (Arr::get($args, 'dispute_note')) {
229 $this->meta = array_merge($this->meta, [
230 'dispute_note' => $args['dispute_note'],
231 ]);
232 $this->save();
233 }
234
235 fluent_cart_add_log(
236 'Dispute accepted on ' . $this->payment_method,
237 'Dispute accepted! ' . $args['dispute_note'] ?? 'Note: ' . Arr::get($args, 'dispute_note'), 'success', [
238 'module_id' => $this->order->id,
239 'module_name' => 'order',
240 ]
241 );
242 } else {
243 return new \WP_Error('invalid_payment_method', __('This payment method does not support remote dispute management', 'fluent-cart'));
244 }
245 }
246
247 public function scopeSearchByPayerEmail ($query, $data) {
248
249 $operator = Arr::get($data, 'operator', 'contains');
250
251 $search = Arr::get($data, 'value');
252 $search = sanitize_text_field(trim($search));
253
254 switch ($operator) {
255 case 'starts_with':
256 $pattern = $search . '%';
257 break;
258 case 'ends_with':
259 $pattern = '%' . $search;
260 break;
261 case 'equals':
262 $pattern = $search;
263 break;
264 case 'not_like':
265 return $query->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.payer.email_address')) NOT LIKE ?", ['%' . $search . '%']);
266 default: // contains
267 $pattern = '%' . $search . '%';
268 break;
269 }
270
271 return $query->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(meta, '$.payer.email_address')) LIKE ?", [$pattern]);
272
273 }
274
275 }
276