improved environment management
This commit is contained in:
70
README.md
70
README.md
@@ -1,52 +1,50 @@
|
|||||||
# qpay-peak-flutter
|
# qpay-peak-flutter
|
||||||
|
|
||||||
## Switch from Test to Live Environment
|
## Environment Switching (Test vs Live)
|
||||||
|
|
||||||
This project currently reads runtime API settings from `assets/.env` and loads Mastercard Checkout for web from `web/index.html`.
|
Environment selection is command-driven with `--dart-define=APP_ENV=...`.
|
||||||
|
|
||||||
Use the checklist below before building for production.
|
- `APP_ENV=test` loads `assets/.env.test`
|
||||||
|
- `APP_ENV=live` loads `assets/.env.live`
|
||||||
|
- If `APP_ENV` is not provided, default is `test`
|
||||||
|
|
||||||
### 1) Update API and simulation flags (`assets/.env`)
|
For web, Mastercard Checkout is loaded dynamically from `GATEWAY_SCRIPT_URL` in the active env file, so `web/index.html` no longer needs manual test/live script edits.
|
||||||
|
|
||||||
In `assets/.env`, make sure the active (uncommented) values are set for live:
|
### Env files
|
||||||
|
|
||||||
```dotenv
|
Set environment-specific values in:
|
||||||
BASE_URL=https://peakapi.qantra.co.zw/api
|
|
||||||
SIMULATE_PAYMENT_SUCCESS=false
|
- `assets/.env.test`
|
||||||
|
- `assets/.env.live`
|
||||||
|
|
||||||
|
Supported keys:
|
||||||
|
|
||||||
|
- `BASE_URL`
|
||||||
|
- `CLIENTID`
|
||||||
|
- `SIMULATE_PAYMENT_SUCCESS`
|
||||||
|
- `GATEWAY_SCRIPT_URL`
|
||||||
|
|
||||||
|
### Run in Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter run --dart-define=APP_ENV=test
|
||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
### Run in Live
|
||||||
- Keep only one active `BASE_URL` line.
|
|
||||||
- `SIMULATE_PAYMENT_SUCCESS` must be `false` in live to avoid fake-success payment flows.
|
|
||||||
|
|
||||||
### 2) Update Mastercard Checkout script (`web/index.html`)
|
```bash
|
||||||
|
flutter run --dart-define=APP_ENV=live
|
||||||
In `web/index.html`, switch the checkout script from test to live:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<script src="https://na.gateway.mastercard.com/static/checkout/checkout.min.js"></script>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
And ensure the test script is not active:
|
### Build in Live
|
||||||
|
|
||||||
```html
|
```bash
|
||||||
<!-- <script src="https://test-gateway.mastercard.com/static/checkout/checkout.min.js"></script> -->
|
flutter build web --dart-define=APP_ENV=live
|
||||||
|
flutter build apk --dart-define=APP_ENV=live
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3) Quick pre-release verification
|
### Quick pre-release checks
|
||||||
|
|
||||||
Before release, confirm:
|
- Confirm `assets/.env.live` points to the live API (`BASE_URL`).
|
||||||
- `assets/.env` points to the live API URL.
|
- Confirm `assets/.env.live` has `SIMULATE_PAYMENT_SUCCESS=false`.
|
||||||
- `assets/.env` has `SIMULATE_PAYMENT_SUCCESS=false`.
|
- Confirm `assets/.env.live` uses live gateway script URL in `GATEWAY_SCRIPT_URL`.
|
||||||
- `web/index.html` loads `na.gateway.mastercard.com` (not `test-gateway.mastercard.com`).
|
|
||||||
|
|
||||||
## Switch back to Test
|
|
||||||
|
|
||||||
To return to test:
|
|
||||||
- Restore the test `BASE_URL` in `assets/.env`.
|
|
||||||
- Set `SIMULATE_PAYMENT_SUCCESS=true` (only if needed for local testing).
|
|
||||||
- Re-enable the test checkout script in `web/index.html`.
|
|
||||||
|
|
||||||
## Source of Truth
|
|
||||||
|
|
||||||
If environment settings change in future, update this README section together with `assets/.env` and `web/index.html` so release steps stay accurate.
|
|
||||||
|
|||||||
5
assets/.env.live
Normal file
5
assets/.env.live
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BASE_URL=https://peakapi.qantra.co.zw/api
|
||||||
|
CLIENTID=77433712483-ng7pntvcpf6tnjccriuqm8dbna8vvp3b.apps.googleusercontent.com
|
||||||
|
SIMULATE_PAYMENT_SUCCESS=false
|
||||||
|
GATEWAY_SCRIPT_URL=https://na.gateway.mastercard.com/static/checkout/checkout.min.js
|
||||||
|
|
||||||
5
assets/.env.test
Normal file
5
assets/.env.test
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BASE_URL=http://192.168.100.85:6950/api
|
||||||
|
CLIENTID=77433712483-ng7pntvcpf6tnjccriuqm8dbna8vvp3b.apps.googleusercontent.com
|
||||||
|
SIMULATE_PAYMENT_SUCCESS=true
|
||||||
|
GATEWAY_SCRIPT_URL=https://test-gateway.mastercard.com/static/checkout/checkout.min.js
|
||||||
|
|
||||||
@@ -1,7 +1,48 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:js_interop';
|
import 'dart:js_interop';
|
||||||
|
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:web/web.dart' as web;
|
||||||
|
|
||||||
|
const String _defaultGatewayScriptUrl =
|
||||||
|
'https://test-gateway.mastercard.com/static/checkout/checkout.min.js';
|
||||||
|
|
||||||
|
@JS('Checkout')
|
||||||
|
external JSAny? _checkout;
|
||||||
|
|
||||||
@JS('configure')
|
@JS('configure')
|
||||||
external void configure(String sessionID);
|
external void configure(String sessionID);
|
||||||
|
|
||||||
@JS('showEmbeddedPage')
|
@JS('showEmbeddedPage')
|
||||||
external void showEmbeddedPage();
|
external void showEmbeddedPage();
|
||||||
|
|
||||||
|
Future<void> ensureCheckoutScriptLoaded() async {
|
||||||
|
final existing = web.document.querySelector('#mastercard-checkout-script');
|
||||||
|
if (existing is web.HTMLScriptElement) {
|
||||||
|
await _waitForCheckout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final scriptUrl =
|
||||||
|
dotenv.env['GATEWAY_SCRIPT_URL'] ?? _defaultGatewayScriptUrl;
|
||||||
|
|
||||||
|
final scriptElement = web.HTMLScriptElement()
|
||||||
|
..id = 'mastercard-checkout-script'
|
||||||
|
..src = scriptUrl
|
||||||
|
..async = true;
|
||||||
|
|
||||||
|
web.document.head?.append(scriptElement);
|
||||||
|
await _waitForCheckout();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _waitForCheckout() async {
|
||||||
|
const maxAttempts = 100;
|
||||||
|
for (var attempt = 0; attempt < maxAttempts; attempt++) {
|
||||||
|
if (_checkout != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw StateError('Mastercard Checkout script failed to load before timeout.');
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,21 +29,34 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'firebase_options.dart';
|
import 'firebase_options.dart';
|
||||||
|
|
||||||
const String testEnv = "assets/.env";
|
const String testEnv = 'assets/.env.test';
|
||||||
const String liveEnv = "assets/.env";
|
const String liveEnv = 'assets/.env.live';
|
||||||
|
const String appEnv = String.fromEnvironment('APP_ENV', defaultValue: 'test');
|
||||||
|
|
||||||
|
String resolveEnvFile(String env) {
|
||||||
|
switch (env.toLowerCase()) {
|
||||||
|
case 'live':
|
||||||
|
return liveEnv;
|
||||||
|
case 'test':
|
||||||
|
default:
|
||||||
|
return testEnv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
options: DefaultFirebaseOptions.currentPlatform,
|
|
||||||
);
|
|
||||||
|
|
||||||
await dotenv.load(fileName: testEnv);
|
await dotenv.load(fileName: resolveEnvFile(appEnv));
|
||||||
runApp(
|
runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
ChangeNotifierProvider<TransactionController>(create: (_) => TransactionController()),
|
ChangeNotifierProvider<TransactionController>(
|
||||||
ChangeNotifierProvider<OnboardingController>(create: (_) => OnboardingController()),
|
create: (_) => TransactionController(),
|
||||||
|
),
|
||||||
|
ChangeNotifierProvider<OnboardingController>(
|
||||||
|
create: (_) => OnboardingController(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const MyApp(),
|
child: const MyApp(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ class _GatewayWebScreenState extends State<GatewayWebScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void initIframeView() async {
|
void initIframeView() async {
|
||||||
|
await ensureCheckoutScriptLoaded();
|
||||||
|
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
// 2. The pathSegments property returns a list of the parts of the path.
|
// 2. The pathSegments property returns a list of the parts of the path.
|
||||||
// For "checkout/pay/SESSION...", the segments are ["checkout", "pay", "SESSION..."].
|
// For "checkout/pay/SESSION...", the segments are ["checkout", "pay", "SESSION..."].
|
||||||
@@ -187,10 +189,8 @@ class _GatewayWebScreenState extends State<GatewayWebScreen> {
|
|||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Once done on this page, check transaction status',
|
'Once done on this page, check transaction status',
|
||||||
@@ -211,9 +211,7 @@ class _GatewayWebScreenState extends State<GatewayWebScreen> {
|
|||||||
.colorScheme
|
.colorScheme
|
||||||
.primary
|
.primary
|
||||||
.withValues(alpha: 0.15),
|
.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(12),
|
||||||
12,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Check status',
|
'Check status',
|
||||||
@@ -224,7 +222,7 @@ class _GatewayWebScreenState extends State<GatewayWebScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10,),
|
SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: constraints.maxHeight + 300,
|
height: constraints.maxHeight + 300,
|
||||||
|
|||||||
@@ -33,11 +33,12 @@
|
|||||||
|
|
||||||
<title>Velocity</title>
|
<title>Velocity</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<script src="https://na.gateway.mastercard.com/static/checkout/checkout.min.js">
|
|
||||||
</script>
|
|
||||||
<!-- <script src="https://test-gateway.mastercard.com/static/checkout/checkout.min.js"></script> -->
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function configure(sessionID) {
|
function configure(sessionID) {
|
||||||
|
if (typeof Checkout === 'undefined') {
|
||||||
|
throw new Error('Checkout SDK not loaded.');
|
||||||
|
}
|
||||||
|
|
||||||
Checkout.configure({
|
Checkout.configure({
|
||||||
session: {
|
session: {
|
||||||
id: sessionID
|
id: sessionID
|
||||||
@@ -46,6 +47,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function showEmbeddedPage() {
|
function showEmbeddedPage() {
|
||||||
|
if (typeof Checkout === 'undefined') {
|
||||||
|
throw new Error('Checkout SDK not loaded.');
|
||||||
|
}
|
||||||
|
|
||||||
Checkout.showEmbeddedPage('#embed-target');
|
Checkout.showEmbeddedPage('#embed-target');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user