# Call Stripe API from Flutter Securely

> A Stripe secret key passed with dart-define or kept in Dart source is compiled into the APK or IPA and can be extracted. Ship only a RealComposer client token in the Flutter app. The edge pipeline injects STRIPE_SECRET_KEY and returns plan and status fields for your billing screen.

- Canonical page: https://realcomposer.com/recipes/call-stripe-from-flutter-securely
- Client platform: Flutter
- Upstream services: Stripe
- Category: Security & Secret Vault
- JSON version: https://realcomposer.com/recipes/call-stripe-from-flutter-securely.json
- Install in RealComposer (free plan, pre-fills this pipeline): https://realcomposer.com/dashboard/pipelines/new?recipe=call-stripe-from-flutter-securely&via=agent

## Problem: Why Flutter apps cannot hold Stripe secret keys

Flutter release builds keep string constants and dart-define values readable, even with obfuscation enabled. A leaked secret key allows full account access, so Stripe server calls must come from a trusted backend or edge.

- sk_live_ recovered from APK or IPA string tables
- Full Stripe customer objects parsed on the UI isolate
- Key rotation forcing a new store release

## Solution steps

1. **Store credentials in Secret Vault**: Save STRIPE_SECRET_KEY in RealComposer so the value never enters the Flutter project.

2. **Configure the edge pipeline**: Add the upstream steps with vault header injection, set allowed origins, and map only the fields the screen binds.

3. **Call the pipeline with package:http**: Call your workspace edge URL with a restricted rc_live_ client token.

```
final res = await http.get(
  Uri.parse('https://realcomposer.com/v1/workspace/call-stripe-from-flutter-securely?customerId=$customerId'),
  headers: {'Authorization': 'Bearer rc_live_client_token'},
);
final billing = jsonDecode(res.body);
```

## Pipeline config

Import this config into a RealComposer pipeline. `{{secrets.NAME}}` values are read from the encrypted Secret Vault at request time, so the client never sees them.

```json
{
  "name": "Call Stripe API from Flutter Securely",
  "method": "GET",
  "cacheTtlSeconds": 0,
  "authMode": "client_token",
  "steps": [
    {
      "stepKey": "stripe",
      "upstreamUrlTemplate": "https://api.stripe.com/v1/customers/{{query.customerId}}?expand[]=subscriptions",
      "httpMethod": "GET",
      "headersTemplate": {
        "Authorization": "Bearer {{secrets.STRIPE_SECRET_KEY}}"
      },
      "timeoutMs": 3000
    }
  ],
  "outputMapping": {
    "version": 1,
    "fields": [
      {
        "targetPath": "plan",
        "sourceStep": "stripe",
        "sourcePath": "subscriptions.data.0.items.data.0.price.nickname"
      },
      {
        "targetPath": "status",
        "sourceStep": "stripe",
        "sourcePath": "subscriptions.data.0.status"
      },
      {
        "targetPath": "renewsAt",
        "sourceStep": "stripe",
        "sourcePath": "subscriptions.data.0.current_period_end",
        "type": "number"
      }
    ]
  }
}
```

## Call the pipeline

```bash
curl -X GET "https://realcomposer.com/v1/workspace/call-stripe-from-flutter-securely" \
  -H "Authorization: Bearer rc_live_client_token"
```

## Payload before and after

Raw upstream response:

```json
{
  "id": "cus_Q2x8Lm",
  "object": "customer",
  "email": "ada@example.com",
  "name": "Ada Lovelace",
  "invoice_settings": {
    "default_payment_method": "pm_1Pz",
    "footer": null
  },
  "metadata": {
    "crm_id": "8812"
  },
  "subscriptions": {
    "object": "list",
    "data": [
      {
        "id": "sub_1Px",
        "status": "active",
        "current_period_end": 1767225600,
        "items": {
          "object": "list",
          "data": [
            {
              "price": {
                "nickname": "Pro"
              }
            }
          ]
        }
      }
    ]
  }
}
```

Slimmed RealComposer response:

```json
{
  "plan": "Pro",
  "status": "active",
  "renewsAt": 1767225600
}
```

## FAQ

### Can I use flutter_stripe without a backend?

The flutter_stripe SDK uses your publishable key on device, but creating PaymentIntents and reading subscriptions still needs the secret key on a server or edge pipeline.

### Does --obfuscate protect the Stripe key?

No. Obfuscation renames symbols but leaves string values intact, so the key can still be extracted.
