Backend:
- Surface a default retail price per code on the public partner-store
payload (config qr.default_retail_price_per_code_centavos, env
QR_DEFAULT_RETAIL_PRICE_PER_CODE_CENTAVOS, default ₱10 = 1000c).
Per-store overrides can come later — the LGU sets one official price
for now.
- Resident-facing flow uses the existing ManualPaymentDriver: resident
POSTs /me/payments/code-purchase, payment is created in pending,
admin marks it paid via /admin/payments/{uuid}/mark-paid, fulfillment
runs StoreOperations::sellToHousehold which activates the codes +
fires the CodesPurchased notification.
Frontend (customer-web/):
- /stores — list nearby partner stores (1/2/5/10/25 km radius pills),
shows in-stock pill + price + distance, links into purchase page
only when has_stock.
- /stores/[uuid] — store detail with stat row (price / stock /
distance) and PurchaseForm: quantity presets (5/10/20/50) + custom
input clamped to inventory, live total computation, blue "cash
payment" callout explaining the flow.
- /payments/[uuid] — three states:
- Pending: ticket-style card with reference + amount + 4-step
instructions; auto-polls every 8 s until paid.
- Paid: success card with checkmark + paid_at timestamp + link
to /codes.
- Failed/refunded: red card with retry CTA back to /stores.
- Resident nav adds "Buy codes" link.
- API client: PartnerStore now carries retail_price_per_code_centavos;
Payment + PaymentStatus types added; payments.show() returns the
typed status union.
Build: 21 routes, all type-check + lint clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
35 lines
1.3 KiB
PHP
35 lines
1.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Resources;
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Resources\Json\JsonResource;
|
|
|
|
/**
|
|
* Resident-facing store payload. Excludes commission rates, owner info,
|
|
* permit number — only what a buyer needs to choose where to go.
|
|
*/
|
|
class PublicPartnerStoreResource extends JsonResource
|
|
{
|
|
public function toArray(Request $request): array
|
|
{
|
|
return [
|
|
'id' => $this->uuid,
|
|
'business_name' => $this->business_name,
|
|
'address_line' => $this->address_line,
|
|
'coordinates' => $this->coordinates ? [
|
|
'lat' => $this->coordinates->latitude,
|
|
'lng' => $this->coordinates->longitude,
|
|
] : null,
|
|
'inventory_balance' => $this->whenLoaded('inventory', fn () => $this->inventory?->current_code_balance ?? 0),
|
|
'has_stock' => $this->whenLoaded('inventory', fn () => ($this->inventory?->current_code_balance ?? 0) > 0),
|
|
'distance_meters' => $this->when(
|
|
isset($this->distance_meters),
|
|
fn () => round((float) $this->distance_meters, 1),
|
|
),
|
|
'barangay' => $this->whenLoaded('barangay', fn () => $this->barangay?->name),
|
|
'retail_price_per_code_centavos' => (int) config('qr.default_retail_price_per_code_centavos', 1000),
|
|
];
|
|
}
|
|
}
|