Verhindert das Anlegen eines zweiten Abos (eigenes oder Kundenabo) wenn bereits eines initiiert ist bzw. ein offener Abo-Payment-Link existiert (redirectIfAboAlreadyInitiated). Payment-Links koennen jetzt geloescht werden, abgesichert ueber canDeleteInstance + Shop-Zugehoerigkeit, mit Erfolgs-/Fehler-Flash. Kundenabo-Uebersicht zeigt den Pending-Initial- Payment-Status (wartend / in Bearbeitung / fehlgeschlagen). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
244 lines
10 KiB
PHP
244 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ShoppingInstance;
|
|
use App\Models\ShoppingUser;
|
|
use App\Models\UserAbo;
|
|
use App\User;
|
|
use Yard;
|
|
|
|
class OrderPaymentService
|
|
{
|
|
public static function deleteInstance($identifier)
|
|
{
|
|
|
|
Yard::instance('shopping')->deleteStoredCart($identifier);
|
|
\App\Models\ShoppingInstance::where('identifier', $identifier)->delete();
|
|
// delete session
|
|
/* if(\Session::has('user_shop_payment') && \Session::get('user_shop_payment') === 6){
|
|
$user_shop_identifier = \Session::get('user_shop_identifier');
|
|
Yard::instance('shopping')->deleteStoredCart($identifier);
|
|
\App\Models\ShoppingInstance::where('identifier', $identifier)->delete();
|
|
}*/
|
|
}
|
|
|
|
public static function isAboCustomerPaymentLink(ShoppingInstance $shoppingInstance): bool
|
|
{
|
|
return (int) $shoppingInstance->payment === 6
|
|
&& ($shoppingInstance->shopping_data['is_for'] ?? null) === 'abo-ot-customer'
|
|
&& (bool) ($shoppingInstance->shopping_data['is_abo'] ?? false);
|
|
}
|
|
|
|
public static function canDeleteInstance(ShoppingInstance $shoppingInstance): bool
|
|
{
|
|
return in_array((int) $shoppingInstance->status, [0, 1, 5, 6], true);
|
|
}
|
|
|
|
public static function isPaymentProcessing(ShoppingInstance $shoppingInstance): bool
|
|
{
|
|
return in_array((int) $shoppingInstance->status, [2, 3, 4], true);
|
|
}
|
|
|
|
public static function isPaymentFailed(ShoppingInstance $shoppingInstance): bool
|
|
{
|
|
return in_array((int) $shoppingInstance->status, [5, 6], true);
|
|
}
|
|
|
|
public static function hasUserAboForPaymentLink(ShoppingInstance $shoppingInstance, User $user): bool
|
|
{
|
|
$shoppingUserId = (int) ($shoppingInstance->shopping_user_id ?: ($shoppingInstance->shopping_data['shopping_user_id'] ?? 0));
|
|
$email = trim((string) ($shoppingInstance->shopping_data['billing_email'] ?? ''));
|
|
|
|
if ($shoppingUserId <= 0 && $email === '') {
|
|
return false;
|
|
}
|
|
|
|
return UserAbo::where('member_id', $user->id)
|
|
->where('is_for', 'ot')
|
|
->where('status', '>=', 1)
|
|
->where(function ($query) use ($shoppingUserId, $email) {
|
|
if ($shoppingUserId > 0) {
|
|
$query->orWhere('shopping_user_id', $shoppingUserId);
|
|
}
|
|
|
|
if ($email !== '') {
|
|
$query->orWhereRaw('LOWER(email) = ?', [mb_strtolower($email)]);
|
|
}
|
|
})
|
|
->exists();
|
|
}
|
|
|
|
public static function updateInstanceStatus($identifier, $status, $lower = true)
|
|
{
|
|
if (! ShoppingInstance::where('identifier', $identifier)->exists()) {
|
|
return false;
|
|
}
|
|
if ($lower) {
|
|
ShoppingInstance::where('identifier', $identifier)->where('status', '<', $status)
|
|
->update(['status' => $status]);
|
|
} else {
|
|
ShoppingInstance::where('identifier', $identifier)
|
|
->update(['status' => $status]);
|
|
}
|
|
}
|
|
|
|
public static function getInstanceStatus($identifier)
|
|
{
|
|
$shopping_instance = ShoppingInstance::where('identifier', $identifier)->first();
|
|
if (! $shopping_instance) {
|
|
return false;
|
|
}
|
|
|
|
return $shopping_instance->getStatus();
|
|
}
|
|
|
|
public static function getTypeBadge(ShoppingInstance $shoppingInstance)
|
|
{
|
|
$isFor = $shoppingInstance->shopping_data['is_for'] ?? '-';
|
|
if ($isFor === 'abo-ot-customer') {
|
|
return ' <span class="badge badge-pill badge-warning">'.__('abo.abo').'</span>';
|
|
}
|
|
if ($isFor === 'ot-customer') {
|
|
return ' <span class="badge badge-pill badge-secondary">'.__('order.order').'</span>';
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
public static function getStatusBadgeClasses()
|
|
{
|
|
return [
|
|
'link_sent' => 'default',
|
|
'link_openly' => 'info',
|
|
'link_check' => 'warning',
|
|
'link_pending' => 'warning',
|
|
'link_appointed' => 'warning',
|
|
'link_paid' => 'secondary',
|
|
'link_failed' => 'danger',
|
|
'link_canceled' => 'danger',
|
|
];
|
|
}
|
|
|
|
public static function getStatusBadge(ShoppingInstance $shoppingInstance)
|
|
{
|
|
$status = $shoppingInstance->getStatus();
|
|
$badgeClasses = self::getStatusBadgeClasses();
|
|
|
|
if (isset($badgeClasses[$status])) {
|
|
return sprintf(
|
|
' <span class="badge badge-pill badge-%s">%s</span>',
|
|
$badgeClasses[$status],
|
|
__('payment.'.$status)
|
|
);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
public static function getStatusAlert($status)
|
|
{
|
|
$badgeClasses = [
|
|
'link_sent' => 'info',
|
|
'link_openly' => 'info',
|
|
'link_check' => 'warning',
|
|
'link_pending' => 'warning',
|
|
'link_failed' => 'danger',
|
|
'link_canceled' => 'danger',
|
|
'link_appointed' => 'warning',
|
|
'link_paid' => 'success',
|
|
|
|
];
|
|
|
|
if (isset($badgeClasses[$status])) {
|
|
return sprintf(
|
|
' <div class="alert alert-%s">%s</div>',
|
|
$badgeClasses[$status],
|
|
__('payment.alert_'.$status)
|
|
);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
public static function getCustomPayment($identifier)
|
|
{
|
|
|
|
$shopping_instance = ShoppingInstance::where('identifier', $identifier)->first();
|
|
if (! $shopping_instance) {
|
|
abort(403, __('msg.shopping_instance_not_found'));
|
|
}
|
|
$shopping_data = $shopping_instance->shopping_data;
|
|
$shopping_user = $shopping_data['shopping_user_id'] ? ShoppingUser::find($shopping_data['shopping_user_id']) : null;
|
|
if (! $shopping_user) {
|
|
abort(403, __('msg.shopping_user_not_found'));
|
|
}
|
|
$yard_shopping_items = self::getRestoredYardShoppingItems($shopping_instance);
|
|
|
|
$data = [
|
|
'shopping_instance' => $shopping_instance,
|
|
'shopping_user' => $shopping_user,
|
|
'yard_shopping_items' => $yard_shopping_items,
|
|
'identifier' => $identifier,
|
|
'is_abo' => $shopping_instance->shopping_data['is_abo'] ?? false,
|
|
'is_for' => $shopping_instance->shopping_data['is_for'] ?? false,
|
|
'backlink' => false,
|
|
];
|
|
|
|
return $data;
|
|
}
|
|
|
|
public static function getRestoredYardShoppingItems($shopping_instance)
|
|
{
|
|
|
|
Yard::instance('shopping')->destroy();
|
|
Yard::instance('shopping')->restore($shopping_instance->identifier, [], false);
|
|
Yard::instance('shopping')->putYardExtra('user_shop_payment', $shopping_instance->payment);
|
|
|
|
Yard::instance('shopping')->putYardExtra('shopping_data', $shopping_instance->shopping_data);
|
|
$is_for = isset($shopping_instance->shopping_data['is_for']) ? $shopping_instance->shopping_data['is_for'] : 'ot-member';
|
|
Yard::instance('shopping')->setUserPriceInfos($shopping_instance->shopping_data['user_price_infos']);
|
|
Yard::instance('shopping')->setShippingCountryWithPrice($shopping_instance->country_id, $is_for);
|
|
|
|
$rows = Yard::instance('shopping')->getContentByOrder();
|
|
$ret = [];
|
|
$ret['items'] = [];
|
|
$is_currency = Yard::instance('shopping')->isPriceCurrency();
|
|
$tax_free = Yard::instance('shopping')->getUserTaxFree();
|
|
|
|
foreach ($rows as $row) {
|
|
$product = \App\Models\Product::find($row->id);
|
|
$item = new \stdClass;
|
|
$item->image = $row->options->has('image') ? $row->options->image : null;
|
|
$item->price_net = (float) Yard::instance('shopping')->rowPriceNet($row, 3, '.', '');
|
|
$item->price_net_total = (float) Yard::instance('shopping')->rowSubtotalNet($row, 2, '.', '');
|
|
$item->price_currency = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('rowPriceNetCurrency', $row, 3).' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$item->price_currency_total = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('rowSubtotalCurrency', $row, 3).' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$item->price = $row->price;
|
|
$item->price_total = ($row->qty * $row->price);
|
|
$item->qty = $row->qty;
|
|
$item->name = $product->name;
|
|
$item->contents = $product->contents;
|
|
$item->numbers = $product->numbers;
|
|
$item->abo_type = AboHelper::getAboShowOn($product);
|
|
$item->number = $product->number;
|
|
$item->contents = $product->contents;
|
|
$ret['items'][] = $item;
|
|
}
|
|
|
|
$ret['tax_free'] = $tax_free;
|
|
$ret['total']['subtotal'] = Yard::instance('shopping')->subtotal();
|
|
$ret['total']['subtotal_currency'] = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('subtotal').' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$ret['total']['shippingCountryName'] = Yard::instance('shopping')->getShippingCountryName();
|
|
$ret['total']['shippingNet'] = Yard::instance('shopping')->shippingNet();
|
|
$ret['total']['shippingNet currency'] = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('shippingNet').' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$ret['total']['subtotalWithShipping'] = Yard::instance('shopping')->subtotalWithShipping();
|
|
$ret['total']['subtotalWithShipping_currency'] = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('subtotalWithShipping').' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$ret['total']['taxWithShipping'] = Yard::instance('shopping')->taxWithShipping();
|
|
$ret['total']['taxWithShipping_currency'] = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('taxWithShipping').' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
$ret['total']['totalWithShipping'] = Yard::instance('shopping')->totalWithShipping();
|
|
$ret['total']['totalWithShipping_currency'] = $is_currency ? '~'.Yard::instance('shopping')->getCurrencyByKey('totalWithShipping').' '.Yard::instance('shopping')->getPriceCurrencyUnit() : null;
|
|
|
|
return $ret;
|
|
}
|
|
}
|