Learn how to integrate Stripe payments without a website for mobile apps, IoT, or backend systems. Step-by-step guide for secure, code-based payment processing.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Integrating Stripe Without a Website: A Comprehensive Tutorial
Introduction
Integrating Stripe without a traditional website is entirely possible and can be valuable for various use cases such as mobile apps, IoT devices, CLI applications, or backend systems that need payment processing capabilities. This tutorial will guide you through the complete process of integrating Stripe's payment processing capabilities without needing a conventional website.
Step 1: Set Up a Stripe Account
Before you begin integration, you need to create and set up a Stripe account:
Step 2: Install the Stripe SDK/Library
Depending on your platform, install the appropriate Stripe library:
For Node.js:
npm install stripe
For Python:
pip install stripe
For PHP:
composer require stripe/stripe-php
For Ruby:
gem install stripe
For Java:
// Add to your pom.xml
com.stripe
stripe-java
20.136.0
Step 3: Initialize the Stripe Client
After installing the library, initialize it with your secret key:
Node.js:
const stripe = require('stripe')('sk_test_YOUR_SECRET_KEY');
Python:
import stripe
stripe.api_key = "sk_test_YOUR_SECRET\_KEY"
PHP:
require\_once 'vendor/autoload.php';
\Stripe\Stripe::setApiKey('sk_test_YOUR_SECRET_KEY');
Ruby:
require 'stripe'
Stripe.api_key = 'sk_test_YOUR_SECRET\_KEY'
Java:
import com.stripe.Stripe;
import com.stripe.model.\*;
import com.stripe.param.\*;
Stripe.apiKey = "sk_test_YOUR_SECRET_KEY";
Step 4: Create a Payment Intent
A PaymentIntent is Stripe's way of representing your intent to collect payment from a customer:
Node.js:
async function createPaymentIntent() {
try {
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000, // Amount in cents
currency: 'usd',
payment_method_types: ['card'],
description: 'Payment for order #12345',
metadata: {
order\_id: '12345',
customer\_name: 'John Doe'
}
});
return paymentIntent;
} catch (error) {
console.error('Error creating payment intent:', error);
throw error;
}
}
Python:
def create_payment_intent():
try:
payment\_intent = stripe.PaymentIntent.create(
amount=1000, # Amount in cents
currency="usd",
payment_method_types=["card"],
description="Payment for order #12345",
metadata={
"order\_id": "12345",
"customer\_name": "John Doe"
}
)
return payment\_intent
except Exception as e:
print(f"Error creating payment intent: {e}")
raise
Step 5: Collect Payment Details Through Your Non-Web Application
Depending on your application type, you'll need to collect payment details. Here's how to do this for different applications:
For a Mobile App (using Stripe SDK):
Android (Kotlin):
// Add dependencies in build.gradle
implementation 'com.stripe:stripe-android:20.8.0'
// In your activity/fragment
private fun setupStripe() {
val paymentConfiguration = PaymentConfiguration.getInstance(applicationContext)
paymentConfiguration.publishableKey = "pk_test_YOUR_PUBLISHABLE_KEY"
val paymentMethodLauncher = PaymentMethodLauncher(
this,
paymentConfiguration.publishableKey,
paymentConfiguration.stripeAccountId
)
// Fetch client secret from your server which creates the PaymentIntent
val clientSecret = getClientSecretFromServer()
paymentMethodLauncher.present(
PaymentMethodLauncher.Configuration(
clientSecret = clientSecret,
merchantDisplayName = "Your Company Name"
)
)
}
iOS (Swift):
// Add to your Podfile
// pod 'Stripe'
import Stripe
class PaymentViewController: UIViewController {
func setupStripe() {
StripeAPI.defaultPublishableKey = "pk_test_YOUR_PUBLISHABLE_KEY"
// Fetch client secret from your server which creates the PaymentIntent
let clientSecret = getClientSecretFromServer()
let paymentIntentParams = STPPaymentIntentParams(clientSecret: clientSecret)
let paymentHandler = STPPaymentHandler.shared()
paymentHandler.confirmPayment(paymentIntentParams, with: self) { (status, paymentIntent, error) in
switch status {
case .succeeded:
print("Payment succeeded")
case .canceled:
print("Payment canceled")
case .failed:
print("Payment failed: (error?.localizedDescription ?? "")")
@unknown default:
print("Unknown status")
}
}
}
}
extension PaymentViewController: STPAuthenticationContext {
func authenticationPresentingViewController() -> UIViewController {
return self
}
}
Step 6: Process the Payment on Your Backend
Once you've collected payment details, process the payment on your backend:
Node.js:
async function processPayment(paymentMethodId, paymentIntentId) {
try {
// If you have a paymentIntentId, confirm it with the payment method
if (paymentIntentId) {
const paymentIntent = await stripe.paymentIntents.confirm(paymentIntentId, {
payment\_method: paymentMethodId,
});
return paymentIntent;
} else {
// Create and confirm the payment intent in one step
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment\_method: paymentMethodId,
confirm: true,
return\_url: 'yourapp://return', // For mobile app deep linking
});
return paymentIntent;
}
} catch (error) {
console.error('Error processing payment:', error);
throw error;
}
}
Python:
def process_payment(payment_method_id, payment_intent\_id=None):
try:
if payment_intent_id:
# If you have a payment_intent_id, confirm it with the payment method
payment\_intent = stripe.PaymentIntent.confirm(
payment_intent_id,
payment_method=payment_method\_id
)
return payment\_intent
else:
# Create and confirm the payment intent in one step
payment\_intent = stripe.PaymentIntent.create(
amount=1000,
currency="usd",
payment_method=payment_method\_id,
confirm=True,
return\_url="yourapp://return" # For mobile app deep linking
)
return payment\_intent
except Exception as e:
print(f"Error processing payment: {e}")
raise
Step 7: Handle Successful and Failed Payments
Implement handlers for successful and failed payments:
Node.js:
function handlePaymentOutcome(paymentIntent) {
switch(paymentIntent.status) {
case 'succeeded':
// Payment successful - update your database, send confirmation, etc.
console.log('Payment succeeded!');
// updateOrderStatus(paymentIntent.metadata.order\_id, 'paid');
// sendConfirmationEmail(paymentIntent.metadata.customer\_email);
break;
case 'requires\_action':
// 3D Secure authentication or other action required
console.log('Additional authentication required');
// Return the client\_secret to your frontend to handle the authentication
return {
requires\_action: true,
client_secret: paymentIntent.client_secret
};
case 'requires_payment_method':
// Payment failed - inform user to try another payment method
console.log('Payment failed, customer should try another payment method');
// updateOrderStatus(paymentIntent.metadata.order_id, 'payment_failed');
break;
default:
console.log(`Unhandled payment intent status: ${paymentIntent.status}`);
}
return {
status: paymentIntent.status,
id: paymentIntent.id
};
}
Python:
def handle_payment_outcome(payment\_intent):
if payment\_intent.status == 'succeeded':
# Payment successful - update your database, send confirmation, etc.
print('Payment succeeded!')
# update_order_status(payment_intent.metadata.order_id, 'paid')
# send_confirmation_email(payment_intent.metadata.customer_email)
return {
'status': payment\_intent.status,
'id': payment\_intent.id
}
elif payment_intent.status == 'requires_action':
# 3D Secure authentication or other action required
print('Additional authentication required')
# Return the client\_secret to your frontend to handle the authentication
return {
'requires\_action': True,
'client_secret': payment_intent.client\_secret
}
elif payment_intent.status == 'requires_payment\_method':
# Payment failed - inform user to try another payment method
print('Payment failed, customer should try another payment method')
# update_order_status(payment_intent.metadata.order_id, 'payment\_failed')
return {
'status': payment\_intent.status,
'id': payment\_intent.id
}
else:
print(f'Unhandled payment intent status: {payment\_intent.status}')
return {
'status': payment\_intent.status,
'id': payment\_intent.id
}
Step 8: Set Up Webhooks for Asynchronous Updates
Webhooks are crucial for receiving updates about payment statuses, especially for asynchronous payments:
Node.js (with Express):
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
// This is your Stripe CLI webhook secret for testing your endpoint locally
const endpointSecret = 'whsec_YOUR_WEBHOOK\_SECRET';
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
} catch (err) {
console.log(`Webhook Error: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment\_intent.succeeded':
const paymentIntent = event.data.object;
// Then define and call a function to handle the successful payment intent
handleSuccessfulPayment(paymentIntent);
break;
case 'payment_intent.payment_failed':
const failedPaymentIntent = event.data.object;
// Then define and call a function to handle the failed payment intent
handleFailedPayment(failedPaymentIntent);
break;
// ... handle other event types as needed
default:
console.log(`Unhandled event type ${event.type}`);
}
// Return a 200 response to acknowledge receipt of the event
res.send();
});
app.listen(4242, () => console.log('Running on port 4242'));
function handleSuccessfulPayment(paymentIntent) {
// Update your database
// Fulfill the order
// Send email confirmation to customer
console.log(`PaymentIntent ${paymentIntent.id} succeeded`);
}
function handleFailedPayment(paymentIntent) {
// Update your database
// Alert the customer that their payment failed
console.log(`PaymentIntent ${paymentIntent.id} failed`);
}
Python (with Flask):
from flask import Flask, request, jsonify
import stripe
app = Flask(**name**)
@app.route('/webhook', methods=['POST'])
def webhook():
payload = request.data
sig\_header = request.headers.get('Stripe-Signature')
endpoint_secret = 'whsec_YOUR_WEBHOOK_SECRET'
try:
event = stripe.Webhook.construct\_event(
payload, sig_header, endpoint_secret
)
except ValueError as e:
# Invalid payload
return jsonify({'error': str(e)}), 400
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return jsonify({'error': str(e)}), 400
# Handle the event
if event['type'] == 'payment\_intent.succeeded':
payment\_intent = event\['data']\['object']
handle_successful_payment(payment\_intent)
elif event['type'] == 'payment_intent.payment_failed':
payment\_intent = event\['data']\['object']
handle_failed_payment(payment\_intent)
# ... handle other event types as needed
else:
print(f'Unhandled event type {event["type"]}')
return jsonify({'success': True})
def handle_successful_payment(payment\_intent):
# Update your database
# Fulfill the order
# Send email confirmation to customer
print(f"PaymentIntent {payment\_intent['id']} succeeded")
def handle_failed_payment(payment\_intent):
# Update your database
# Alert the customer that their payment failed
print(f"PaymentIntent {payment\_intent['id']} failed")
if **name** == '**main**':
app.run(port=4242)
Step 9: Implement Other Payment Flows (Optional)
Create a Subscription:
// Node.js
async function createSubscription(customerId, priceId) {
try {
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: priceId }],
payment_behavior: 'default_incomplete',
expand: ['latest_invoice.payment_intent'],
});
return {
subscriptionId: subscription.id,
clientSecret: subscription.latest_invoice.payment_intent.client\_secret,
};
} catch (error) {
console.error('Error creating subscription:', error);
throw error;
}
}
Create a Payment Link:
// Node.js
async function createPaymentLink() {
try {
const paymentLink = await stripe.paymentLinks.create({
line\_items: [
{
price: 'price_YOUR_PRICE\_ID',
quantity: 1,
},
],
after\_completion: {
type: 'redirect',
redirect: {
url: 'yourapp://success',
},
},
});
return paymentLink;
} catch (error) {
console.error('Error creating payment link:', error);
throw error;
}
}
Step 10: Testing Your Integration
To ensure your integration works correctly, perform these tests:
# Install Stripe CLI from https://stripe.com/docs/stripe-cli
stripe login
stripe listen --forward-to localhost:4242/webhook
Step 11: Go Live with Your Integration
When you're ready to go live:
Conclusion
You've now successfully set up Stripe integration without a traditional website. This approach works for mobile apps, IoT devices, backend systems, or any non-web application that needs payment processing capabilities. Remember to always keep your secret API keys secure and never expose them in client-side code.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.