Mobile
Flutter / Dart SDK
Dart implementation for integrating YardPay Pro into Flutter apps on iOS, Android, and web. Uses dart:io / http with no third-party dependencies.
Dependencies
Add the built-in http package to your pubspec.yaml (included with Flutter):
dependencies: http: ^1.1.0
API Client
Create lib/yardpaypro/client.dart
// lib/yardpaypro/client.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class YppException implements Exception {
final int status;
final String code;
final String message;
YppException(this.status, this.code, this.message);
@override
String toString() => 'YppException($status $code: $message)';
}
class YardPayProClient {
final String apiKey;
final String baseUrl;
final http.Client _http;
YardPayProClient({
required this.apiKey,
required this.baseUrl,
http.Client? httpClient,
}) : _http = httpClient ?? http.Client();
Map<String, String> get _headers => {
'Content-Type': 'application/json',
'x-api-key': apiKey,
};
Future<dynamic> request(String method, String path, {
Map<String, dynamic>? body,
Map<String, String>? params,
}) async {
var uri = Uri.parse('$baseUrl$path');
if (params != null && params.isNotEmpty) {
uri = uri.replace(queryParameters: params);
}
late http.Response response;
final bodyStr = body != null ? jsonEncode(body) : null;
switch (method) {
case 'GET':
response = await _http.get(uri, headers: _headers);
break;
case 'POST':
response = await _http.post(uri, headers: _headers, body: bodyStr);
break;
case 'PUT':
response = await _http.put(uri, headers: _headers, body: bodyStr);
break;
case 'DELETE':
response = await _http.delete(uri, headers: _headers);
break;
default:
throw ArgumentError('Unknown method: $method');
}
if (response.statusCode == 204) return null;
if (response.statusCode >= 400) {
final err = jsonDecode(response.body) as Map<String, dynamic>;
throw YppException(
response.statusCode,
err['code']?.toString() ?? 'api_error',
err['message']?.toString() ?? response.reasonPhrase ?? 'Unknown error',
);
}
final data = jsonDecode(response.body);
if (data is Map && data.containsKey('value')) return data['value'];
return data;
}
Future<dynamic> get(String path, [Map<String, String>? params]) =>
request('GET', path, params: params);
Future<dynamic> post(String path, Map<String, dynamic> body) =>
request('POST', path, body: body);
Future<dynamic> put(String path, Map<String, dynamic> body) =>
request('PUT', path, body: body);
Future<dynamic> delete(String path) =>
request('DELETE', path);
void dispose() => _http.close();
}Payments Service
Create lib/yardpaypro/payments.dart
// lib/yardpaypro/payments.dart
import 'client.dart';
class PaymentsService {
final YardPayProClient client;
PaymentsService(this.client);
/// Pay an invoice with a card or saved funding source
Future<Map<String, dynamic>> payInvoice({
int? invoiceId,
String? invoiceIdentifier,
required int paymentOptionId,
int? fundingSourceId,
Map<String, String>? card,
Map<String, String>? billingAddress,
}) async {
return await client.post('/api/payment/payinvoice', {
if (invoiceId != null) 'invoiceId': invoiceId,
if (invoiceIdentifier != null) 'invoiceIdentifier': invoiceIdentifier,
'paymentOptionId': paymentOptionId,
if (fundingSourceId != null) 'fundingSourceId': fundingSourceId,
if (card != null) 'card': card,
if (billingAddress != null) 'billingAddress': billingAddress,
});
}
/// Get available payment methods for an invoice
Future<List<dynamic>> getPaymentMethods(int invoiceId) async {
return await client.get('/v1/invoice/$invoiceId/PaymentMethods');
}
/// List saved funding sources
Future<List<dynamic>> getFundingSources() async {
return await client.get('/v1/FundingSource');
}
/// Register a new card
Future<Map<String, dynamic>> registerCard({
required Map<String, String> card,
required String name,
}) async {
return await client.post('/v1/payment/RegisterCard', {
'card': card,
'name': name,
});
}
/// Verify a registered card
Future<void> verifyCard(int fundingSourceId, String code) async {
await client.post('/v1/fundingSource/$fundingSourceId/verify', {
'VerificationCode': code,
});
}
}Invoices Service
Create lib/yardpaypro/invoices.dart
// lib/yardpaypro/invoices.dart
import 'client.dart';
class InvoicesService {
final YardPayProClient client;
InvoicesService(this.client);
/// Get invoice by public reference code (no auth needed)
Future<Map<String, dynamic>> getByReference(String code) async {
return await client.get("/v1/invoice/getByReference(code='$code')");
}
/// Get invoice by ID
Future<Map<String, dynamic>> getById(int id, {String? expand}) async {
return await client.get(
'/v1/invoice/$id',
expand != null ? {'\$expand': expand} : null,
);
}
/// List invoices with OData filtering
Future<List<dynamic>> list({
String? filter,
String? orderBy,
int top = 20,
int skip = 0,
}) async {
return await client.get('/v1/invoice', {
if (filter != null) '\$filter': filter,
if (orderBy != null) '\$orderby': orderBy,
'\$top': top.toString(),
'\$skip': skip.toString(),
});
}
/// Create a new invoice
Future<Map<String, dynamic>> create(Map<String, dynamic> data) async {
return await client.post('/v1/invoice', data);
}
/// Get QR code URL for an invoice
String getQrCodeUrl(String identifier) {
return '${client.baseUrl}/v1/invoice/qrcode/$identifier';
}
/// Check if an invoice can be paid
bool canBePaid(Map<String, dynamic> invoice) {
const payable = ['Pending', 'Sent', 'PartiallyPaid', 'Overdue'];
return payable.contains(invoice['status']);
}
}Usage in a Flutter Widget
import 'package:flutter/material.dart';
import 'yardpaypro/client.dart';
import 'yardpaypro/payments.dart';
import 'yardpaypro/invoices.dart';
class PaymentScreen extends StatefulWidget {
final String invoiceCode;
const PaymentScreen({super.key, required this.invoiceCode});
@override
State<PaymentScreen> createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
final _client = YardPayProClient(apiKey: 'sk_test_YOUR_KEY', baseUrl: 'https://your-server.com');
late final _payments = PaymentsService(_client);
late final _invoices = InvoicesService(_client);
Map<String, dynamic>? _invoice;
bool _loading = true;
String? _error;
@override
void initState() {
super.initState();
_loadInvoice();
}
Future<void> _loadInvoice() async {
try {
final invoice = await _invoices.getByReference(widget.invoiceCode);
setState(() { _invoice = invoice; _loading = false; });
} on YppException catch (e) {
setState(() { _error = e.message; _loading = false; });
}
}
Future<void> _pay() async {
if (_invoice == null) return;
setState(() => _loading = true);
try {
final result = await _payments.payInvoice(
invoiceIdentifier: _invoice!['identifier'],
paymentOptionId: 1,
card: {
'name': 'Test User',
'cardNumber': '4111111111111111',
'expiryDate': '12/28',
'cvv': '123',
},
);
if (result['isSuccess'] == true) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Paid: ${result["referenceNumber"]}')),
);
}
}
} on YppException catch (e) {
setState(() { _error = e.message; _loading = false; });
}
}
@override
void dispose() {
_client.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (_loading) return const Center(child: CircularProgressIndicator());
if (_error != null) return Center(child: Text('Error: $_error'));
return Scaffold(
appBar: AppBar(title: const Text('Pay Invoice')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_invoice!['description'] ?? 'Invoice', style: Theme.of(context).textTheme.titleLarge),
const SizedBox(height: 8),
Text('${_invoice!["currencyCode"]} ${_invoice!["totalCost"]}',
style: Theme.of(context).textTheme.headlineMedium),
const Spacer(),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _pay,
child: const Text('Pay Now'),
),
),
],
),
),
);
}
}Requirements
- Dart 3.0+ / Flutter 3.10+
- Only dependency:
http(included with Flutter) - Works on iOS, Android, Web, macOS, Windows, Linux