The Wallet API provides native mobile payment integration through Apple Pay (iOS) and Google Pay (Android) using Stripe as the payment processor.
This feature requires a Stripe account and proper configuration for Apple Pay and Google Pay.
Usage
Process a payment using native mobile wallets:
use Native\Mobile\Facades\MobileWallet;
// Check if wallet is available
if (MobileWallet::isAvailable()) {
// Create payment intent
$intent = MobileWallet::createPaymentIntent(
amount: 2999, // $29.99 in cents
currency: 'usd'
);
// Present payment sheet
$result = MobileWallet::presentPaymentSheet(
clientSecret: $intent->client_secret,
merchantDisplayName: 'Your Store Name',
publishableKey: config('services.stripe.key'),
merchantId: 'merchant.com.yourapp'
);
}
Checking Availability
Before attempting to use mobile wallets, check if they’re available:
use Native\Mobile\Facades\MobileWallet;
if (MobileWallet::isAvailable()) {
// Apple Pay or Google Pay is set up and ready
// Show wallet payment button
} else {
// Wallet not available on this device
// Show alternative payment methods
}
A wallet might not be available if:
- Device doesn’t support it (older devices)
- User hasn’t set up a payment method in their wallet
- Feature is disabled in device settings
Payment Flow
The complete payment flow involves several steps:
1. Create Payment Intent
First, create a Stripe payment intent on your server:
$intent = MobileWallet::createPaymentIntent(
amount: 5000, // $50.00 in cents
currency: 'usd',
metadata: [
'user_id' => auth()->id(),
'order_id' => $order->id,
]
);
$clientSecret = $intent->client_secret;
2. Present Payment Sheet
Show the native payment sheet to the user:
$result = MobileWallet::presentPaymentSheet(
clientSecret: $clientSecret,
merchantDisplayName: 'My Store',
publishableKey: config('services.stripe.publishable_key'),
merchantId: 'merchant.com.mystore.app',
merchantCountryCode: 'US',
options: [
'allowsDelayedPaymentMethods' => false,
]
);
3. Handle Result
The payment sheet is modal - check the result afterwards:
if ($result && isset($result->paymentIntent)) {
// Payment successful
$paymentIntentId = $result->paymentIntent->id;
} else {
// Payment was cancelled or failed
}
4. Confirm Payment (Optional)
For some payment flows, you may need to explicitly confirm:
$confirmation = MobileWallet::confirmPayment($paymentIntentId);
if ($confirmation && $confirmation->status === 'succeeded') {
// Payment confirmed
}
5. Check Payment Status
Verify the final payment status:
$status = MobileWallet::getPaymentStatus($paymentIntentId);
if ($status && $status->status === 'succeeded') {
// Process order
}
Methods Reference
isAvailable()
Checks if Apple Pay or Google Pay is available on the device.
Returns: bool
$available = MobileWallet::isAvailable();
Creates a Stripe payment intent for the transaction.
Parameters:
$amount (int) - Amount in smallest currency unit (cents for USD)
$currency (string) - Three-letter ISO currency code (lowercase)
$metadata (array) - Additional metadata to attach to the payment
Returns: object|null - Payment intent data including client_secret
$intent = MobileWallet::createPaymentIntent(
amount: 2499,
currency: 'usd',
metadata: [
'order_id' => '12345',
'customer_id' => 'cust_123',
]
);
$clientSecret = $intent->client_secret;
presentPaymentSheet(string $clientSecret, string $merchantDisplayName, string $publishableKey, string $merchantId, string $merchantCountryCode = 'US', array $options = [])
Presents the native payment sheet for card/wallet selection.
Parameters:
$clientSecret (string) - The client secret from the payment intent
$merchantDisplayName (string) - The merchant name to display
$publishableKey (string) - The Stripe publishable key
$merchantId (string) - The Apple Pay merchant ID (e.g., “merchant.com.yourapp”)
$merchantCountryCode (string) - ISO country code (default: “US”)
$options (array) - Additional options for the payment sheet
Returns: object|null - Result of presenting the payment sheet
$result = MobileWallet::presentPaymentSheet(
clientSecret: $intent->client_secret,
merchantDisplayName: 'Acme Store',
publishableKey: 'pk_live_...',
merchantId: 'merchant.com.acmestore',
merchantCountryCode: 'US',
options: []
);
confirmPayment(string $paymentIntentId)
Confirms the payment with the selected payment method.
Parameters:
$paymentIntentId (string) - The payment intent ID to confirm
Returns: object|null - Confirmation result
$confirmation = MobileWallet::confirmPayment('pi_123456');
getPaymentStatus(string $paymentIntentId)
Retrieves the current payment status.
Parameters:
$paymentIntentId (string) - The payment intent ID to check
Returns: object|null - Payment status information
$status = MobileWallet::getPaymentStatus('pi_123456');
if ($status->status === 'succeeded') {
// Payment successful
}
Complete Example
use Livewire\Component;
use Livewire\Attributes\On;
use Native\Mobile\Facades\MobileWallet;
use Native\Mobile\Events\Wallet\PaymentCompleted;
use Native\Mobile\Events\Wallet\PaymentFailed;
use Native\Mobile\Events\Wallet\PaymentCancelled;
class Checkout extends Component
{
public $order;
public $isProcessing = false;
public $walletAvailable = false;
public function mount($orderId)
{
$this->order = Order::findOrFail($orderId);
$this->walletAvailable = MobileWallet::isAvailable();
}
public function payWithWallet()
{
if (!$this->walletAvailable) {
session()->flash('error', 'Mobile wallet not available');
return;
}
$this->isProcessing = true;
try {
// Create payment intent
$intent = MobileWallet::createPaymentIntent(
amount: (int) ($this->order->total * 100), // Convert to cents
currency: 'usd',
metadata: [
'order_id' => $this->order->id,
'user_id' => auth()->id(),
]
);
if (!$intent) {
throw new \Exception('Failed to create payment intent');
}
// Store payment intent ID for later
$this->order->update([
'stripe_payment_intent_id' => $intent->id,
]);
// Present payment sheet
$result = MobileWallet::presentPaymentSheet(
clientSecret: $intent->client_secret,
merchantDisplayName: config('app.name'),
publishableKey: config('services.stripe.key'),
merchantId: config('services.stripe.merchant_id'),
merchantCountryCode: 'US'
);
// Check immediate result
if ($result && isset($result->error)) {
throw new \Exception($result->error->message ?? 'Payment failed');
}
} catch (\Exception $e) {
$this->isProcessing = false;
session()->flash('error', 'Payment error: ' . $e->getMessage());
}
}
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentCompleted')]
public function handlePaymentCompleted($data)
{
$paymentIntentId = $data['paymentIntentId'] ?? null;
if ($paymentIntentId === $this->order->stripe_payment_intent_id) {
// Verify payment status
$status = MobileWallet::getPaymentStatus($paymentIntentId);
if ($status && $status->status === 'succeeded') {
// Update order
$this->order->update([
'status' => 'paid',
'paid_at' => now(),
]);
$this->isProcessing = false;
// Redirect to success page
return redirect()->route('orders.success', $this->order);
}
}
}
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentFailed')]
public function handlePaymentFailed($data)
{
$this->isProcessing = false;
$error = $data['error'] ?? 'Payment failed';
session()->flash('error', $error);
}
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentCancelled')]
public function handlePaymentCancelled()
{
$this->isProcessing = false;
session()->flash('message', 'Payment cancelled');
}
public function render()
{
return view('livewire.checkout');
}
}
Events
Listen for payment events in your Livewire components:
use Livewire\Attributes\On;
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentCompleted')]
public function handlePaymentCompleted($data)
{
$paymentIntentId = $data['paymentIntentId'];
// Handle successful payment
}
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentFailed')]
public function handlePaymentFailed($data)
{
$error = $data['error'];
// Handle failed payment
}
#[On('native:Native\\Mobile\\Events\\Wallet\\PaymentCancelled')]
public function handlePaymentCancelled()
{
// User cancelled payment
}
iOS (Apple Pay)
Requirements:
- Apple Developer account with Apple Pay enabled
- Merchant ID configured in Apple Developer portal
- Payment processing certificate
- App configured with Apple Pay capability
Xcode Configuration:
- Enable “Apple Pay” capability
- Add merchant ID to entitlements
- Configure merchant identifier
Info.plist:
<key>com.apple.developer.in-app-payments</key>
<array>
<string>merchant.com.yourapp</string>
</array>
Stripe Configuration:
- Register merchant ID with Stripe
- Configure Apple Pay in Stripe Dashboard
- Verify domain ownership
Android (Google Pay)
Requirements:
- Google Play Console account
- Production app published on Play Store
- Google Pay API enabled
- Stripe account configured for Google Pay
AndroidManifest.xml:
<meta-data
android:name="com.google.android.gms.wallet.api.enabled"
android:value="true" />
Gradle:
dependencies {
implementation 'com.google.android.gms:play-services-wallet:19.1.0'
implementation 'com.stripe:stripe-android:20.x.x'
}
Stripe Configuration:
- Enable Google Pay in Stripe Dashboard
- Add Google Pay merchant ID
- Configure payment methods
Currency Support
Specify currency using three-letter ISO codes (lowercase):
// USD (US Dollar)
MobileWallet::createPaymentIntent(1000, 'usd');
// EUR (Euro)
MobileWallet::createPaymentIntent(1000, 'eur');
// GBP (British Pound)
MobileWallet::createPaymentIntent(1000, 'gbp');
// JPY (Japanese Yen - no decimal places)
MobileWallet::createPaymentIntent(1000, 'jpy'); // ¥1,000
Some currencies (like JPY) don’t use decimal places. $10 USD = 1000 cents, but ¥1000 JPY = 1000 (not 100000).
Best Practices
- Always Check Availability: Don’t show wallet buttons if not available
@if(MobileWallet::isAvailable())
<button wire:click="payWithWallet">Pay with Apple/Google Pay</button>
@endif
-
Handle All States: Listen for completed, failed, and cancelled events
-
Verify on Server: Always verify payment status on your server before fulfilling orders
-
Use Metadata: Store order information in payment intent metadata
-
Provide Feedback: Show loading states during payment processing
-
Handle Errors Gracefully: Show user-friendly error messages
Testing
iOS Simulator
- Use test cards in Wallet app
- Stripe provides test card numbers
- Requires Xcode sandbox environment
Android Emulator
- Use test Google accounts
- Configure test payment methods
- Requires Google Play Services
Stripe Test Mode
- Use test publishable key:
pk_test_...
- Use test secret key:
sk_test_...
- Test card:
4242 4242 4242 4242