create([ 'type' => 'endcustomer', 'paddle_price_id' => 'pri_pkg_001', 'price' => 59, ]); $payload = [ 'id' => 'txn_123', 'event_type' => 'transaction.completed', 'currency_code' => 'EUR', 'totals' => [ 'grand_total' => [ 'amount' => 5900, ], ], 'custom_data' => [ 'type' => 'gift_card', 'purchaser_email' => 'buyer@example.com', 'recipient_email' => 'friend@example.com', 'recipient_name' => 'Friend', 'message' => 'Happy Day', ], 'checkout_id' => 'chk_abc', ]; Bus::fake([SyncCouponToPaddle::class]); $service = $this->app->make(GiftVoucherService::class); $voucher = $service->issueFromPaddle($payload); $this->assertInstanceOf(GiftVoucher::class, $voucher); $this->assertSame(59.00, (float) $voucher->amount); $this->assertNotNull($voucher->coupon); $this->assertSame($voucher->code, $voucher->coupon->code); $this->assertTrue($voucher->expires_at->greaterThan(now()->addYears(4))); $this->assertTrue($voucher->coupon->packages()->whereKey($package->id)->exists()); Bus::assertDispatched(SyncCouponToPaddle::class); } public function test_redeeming_coupon_marks_voucher_redeemed(): void { $voucher = GiftVoucher::factory()->create([ 'status' => GiftVoucher::STATUS_ISSUED, 'amount' => 29, ]); $coupon = Coupon::factory()->create([ 'code' => $voucher->code, 'type' => CouponType::FLAT, 'amount' => 29, 'currency' => 'EUR', 'paddle_discount_id' => null, ]); $voucher->coupon()->associate($coupon)->save(); $service = $this->app->make(GiftVoucherService::class); $service->markRedeemed($coupon, 'txn_999'); $this->assertSame(GiftVoucher::STATUS_REDEEMED, $voucher->refresh()->status); $this->assertNotNull($voucher->redeemed_at); } public function test_it_sends_notifications_to_purchaser_and_recipient_once(): void { Mail::fake(); Bus::fake([SyncCouponToPaddle::class]); config()->set('gift-vouchers.reminder_days', 0); config()->set('gift-vouchers.expiry_reminder_days', 0); Package::factory()->create([ 'type' => 'endcustomer', 'paddle_price_id' => 'pri_pkg_001', 'price' => 29, ]); $payload = [ 'id' => 'txn_456', 'currency_code' => 'EUR', 'totals' => [ 'grand_total' => [ 'amount' => 2900, ], ], 'custom_data' => [ 'type' => 'gift_voucher', 'purchaser_email' => 'buyer@example.com', 'recipient_email' => 'friend@example.com', 'app_locale' => 'de', ], 'checkout_id' => 'chk_notif', ]; $service = $this->app->make(GiftVoucherService::class); $voucher = $service->issueFromPaddle($payload); Mail::assertQueued(GiftVoucherIssued::class, 2); $this->assertTrue((bool) ($voucher->metadata['notifications_sent'] ?? false)); // Second call (duplicate webhook) should not resend $service->issueFromPaddle($payload); Mail::assertQueued(GiftVoucherIssued::class, 2); } public function test_it_resolves_amount_from_tier_by_price_id(): void { config()->set('gift-vouchers.tiers', [ [ 'key' => 'gift-standard-usd', 'label' => 'Gift Standard (USD)', 'amount' => 65.00, 'currency' => 'USD', 'paddle_price_id' => 'pri_usd_123', ], ]); Bus::fake([SyncCouponToPaddle::class]); Mail::fake(); $payload = [ 'id' => 'txn_usd', 'price_id' => 'pri_usd_123', 'currency_code' => 'USD', ]; $service = $this->app->make(GiftVoucherService::class); $voucher = $service->issueFromPaddle($payload); $this->assertSame(65.00, (float) $voucher->amount); $this->assertSame('USD', $voucher->currency); } }