Developer-friendly & type-safe Swift SDK specifically catered to leverage Gr4vy API.
The official Gr4vy SDK for Swift provides a convenient way to interact with the Gr4vy API from your iOS application. This SDK allows you to seamlessly integrate Gr4vy's powerful payment orchestration capabilities.
This SDK is designed to simplify development, reduce boilerplate code, and help you get up and running with Gr4vy quickly and efficiently. It handles authentication, request management, and provides easy-to-use async/await methods for all API endpoints.
A SwiftUI client app and UIKit client app that uses this SDK are available for demo and testing purposes.
- SDK Installation
- SDK Example Usage
- Merchant account ID selection
- Timeout Configuration
- Available Operations
- 3D Secure Authentication
- Error Handling
- Server Selection
- Debugging
- Support
- License
iOS 16.0+ is required.
The samples below show how the published SDK artifact is used:
- iOS 16.0+
- Swift 5.7+
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/gr4vy/gr4vy-swift.git", from: "1.0.1")
]Add the following to your Podfile:
pod 'gr4vy-swift', '~> 1.0.1'Then run:
pod installimport gr4vy_swift
do {
let gr4vy = try Gr4vy(
gr4vyId: "example",
token: "your_jwt_token", // Optional
merchantId: "merchant_123", // Optional
server: .sandbox,
debugMode: true // Optional
)
// Create payment options request
let request = Gr4vyPaymentOptionRequest(
merchantId: "merchant_123",
metadata: ["order_id": "12345"],
country: "US",
currency: "USD",
amount: 1299,
locale: "en-US",
cartItems: nil
)
// Get payment options using async/await
let paymentOptions = try await gr4vy.paymentOptions.list(request: request)
print("Available payment options: \(paymentOptions.count)")
} catch {
print("Error: \(error)")
}Depending on the API used, you might need to explicitly define a merchant account ID to use. When using the SDK, you can set the merchantId
at the SDK level, or, on some requests directly.
let request = Gr4vyPaymentOptionRequest(
merchantId: "merchant_123",
metadata: ["order_id": "12345"],
country: "US",
currency: "USD",
amount: 1299,
locale: "en-US",
cartItems: nil
)Alternatively, the merchant account ID can also be set when initializing the SDK.
let gr4vy = try Gr4vy(
gr4vyId: "example",
token: "your_jwt_token",
merchantId: "merchant_123", // Set the default merchant ID
server: .sandbox,
debugMode: true
)The SDK supports configuring request timeouts both at the SDK level (for all requests) and per individual request. This allows you to control how long the SDK will wait for API responses before timing out.
You can set a default timeout for all requests when initializing the SDK. This timeout will be used for all API calls unless overridden at the request level.
let gr4vy = try Gr4vy(
gr4vyId: "example",
token: "your_jwt_token",
merchantId: "merchant_123",
server: .sandbox,
timeout: 45.0, // Set default timeout to 45 seconds
debugMode: true
)You can override the SDK-level timeout for individual requests by specifying the timeout parameter in request objects:
// Payment Options with custom timeout
let paymentOptionsRequest = Gr4vyPaymentOptionRequest(
merchantId: "merchant_123",
metadata: ["order_id": "12345"],
country: "US",
currency: "USD",
amount: 1299,
locale: "en-US",
cartItems: nil,
timeout: 60.0 // Override to 60 seconds for this request
)
// Card Details with custom timeout
let cardDetailsRequest = Gr4vyCardDetailsRequest(
cardDetails: cardDetails,
timeout: 20.0 // Override to 20 seconds for this request
)
// Buyers Payment Methods with custom timeout
let buyersRequest = Gr4vyBuyersPaymentMethodsRequest(
paymentMethods: paymentMethods,
merchantId: "merchant_123",
timeout: 30.0 // Override to 30 seconds for this request
)- SDK Default: 30 seconds (if not specified during initialization)
- Request Override: Uses SDK default if not specified per request
Note: Timeout values are specified in seconds as
TimeInterval(Double).
Stores the card details you collected into a Gr4vy checkout session without 3D Secure authentication.
// Create card data
let cardData = Gr4vyCardData(
paymentMethod: .card(CardPaymentMethod(
number: "4111111111111111",
expirationDate: "12/25",
securityCode: "123"
))
)
// Tokenize card data into checkout session
do {
try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData
)
print("Payment method tokenization complete")
} catch {
print("Error tokenizing payment method: \(error)")
}
// Completion handler
gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData
) { result in
switch result {
case .success:
print("Payment method tokenization complete")
case .failure(let error):
print("Error tokenizing payment method: \(error)")
}
}You can also tokenize an already-stored payment method by passing an id payment method to the same tokenize call. Include a securityCode only when required by the merchant or card scheme.
// Use a stored payment method ID (optionally include security code)
let storedCardData = Gr4vyCardData(
paymentMethod: .id(IdPaymentMethod(
id: "b7e3a2c2-1f4b-4e8a-9c2d-2e7e2b8e9c2d", // stored payment method id (UUID)
securityCode: "123" // optional
))
)
// Tokenize stored payment method (async/await)
do {
try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: storedCardData
)
print("Stored payment method tokenization complete")
} catch {
print("Error tokenizing stored payment method: \(error)")
}
// Completion handler variant
gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: storedCardData
) { result in
switch result {
case .success:
print("Stored payment method tokenization complete")
case .failure(let error):
print("Error tokenizing stored payment method: \(error)")
}
}Stores card details with optional 3D Secure authentication. When authentication is enabled, the SDK will automatically handle 3DS Secure flows.
// Create card data
let cardData = Gr4vyCardData(
paymentMethod: .card(CardPaymentMethod(
number: "4111111111111111",
expirationDate: "12/25",
securityCode: "123"
))
)
// Tokenize with 3DS authentication (async/await)
do {
let result = try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData,
sdkMaxTimeoutMinutes: 5,
authenticate: true
)
if result.tokenized {
print("Payment method tokenized successfully")
// Check authentication details
if let auth = result.authentication {
print("3DS attempted: \(auth.attempted)")
print("Authentication type: \(auth.type ?? "N/A")")
print("Transaction status: \(auth.transactionStatus ?? "N/A")")
if auth.hasCancelled {
print("User cancelled authentication")
}
if auth.hasTimedOut {
print("Authentication timed out")
}
}
}
} catch {
print("Error tokenizing payment method: \(error)")
}
// With explicit view controller (async/await)
do {
let result = try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData,
viewController: self, // Explicitly provide the presenting view controller
sdkMaxTimeoutMinutes: 5,
authenticate: true
)
print("Tokenization complete: \(result.tokenized)")
} catch {
print("Error: \(error)")
}
// Completion handler variant
gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData,
sdkMaxTimeoutMinutes: 5,
authenticate: true
) { result in
switch result {
case .success(let tokenizeResult):
if tokenizeResult.tokenized {
print("Payment method tokenized successfully")
if let auth = tokenizeResult.authentication {
print("Transaction status: \(auth.transactionStatus ?? "N/A")")
}
}
case .failure(let error):
print("Error tokenizing payment method: \(error)")
}
}Parameters:
checkoutSessionId: The checkout session ID from Gr4vycardData: The payment method data to tokenizeviewController: (Optional) The view controller to present the 3DS challenge. If not provided, the SDK will automatically resolve the topmost view controllersdkMaxTimeoutMinutes: Maximum time for 3DS authentication in minutes (default: 5)authenticate: This controls if we should attempt to authenticate the card data (default: false)uiCustomization: (Optional) UI customization for the 3DS challenge screen
Returns: Gr4vyTokenizeResult containing:
tokenized: Boolean indicating if tokenization was successfulauthentication: OptionalGr4vyAuthenticationobject with authentication details
The authentication object contains the following properties:
| Property | Type | Description |
|---|---|---|
transactionStatus |
"Y" |
The transaction was successful |
"N" |
The transaction failed. | |
null |
The transaction status is unknown | |
type |
null |
The flow failed because the card used is not enrolled |
"challenge" |
The flow required the user to perform a challenge | |
"frictionless" |
The flow was successful without requiring a challenge | |
"error" |
The flow encountered an error during the process | |
attempted |
boolean |
Indicates if authentication was attempted |
hasTimedOut |
boolean |
Indicates if the authentication process timed out |
hasCancelled |
boolean |
Indicates if the user cancelled the challenge |
List the available payment options that can be presented at checkout.
// Create request
let request = Gr4vyPaymentOptionRequest(
merchantId: "merchant_123", // Optional, uses SDK merchantId if not provided
metadata: ["order_id": "12345"],
country: "US",
currency: "USD",
amount: 1299,
locale: "en-US",
cartItems: nil
)
// Async/await
do {
let paymentOptions = try await gr4vy.paymentOptions.list(request: request)
print("Available payment options: \(paymentOptions.count)")
} catch {
print("Error fetching payment options: \(error)")
}
// Completion handler
gr4vy.paymentOptions.list(request: request) { result in
switch result {
case .success(let paymentOptions):
print("Available payment options: \(paymentOptions.count)")
case .failure(let error):
print("Error fetching payment options: \(error)")
}
}Get details about a particular card based on its BIN, the checkout country/currency, and more.
Note: Some fields in the response (such as scheme, cardType, type) are optional and may be nil if not provided by the API. Always use optional binding or nil coalescing when accessing these fields.
// Create card details object
let cardDetails = Gr4vyCardDetails(
currency: "USD",
amount: "1299",
bin: "411111",
country: "US",
intent: "capture"
)
// Create request
let request = Gr4vyCardDetailsRequest(
cardDetails: cardDetails,
timeout: 30.0
)
// Async/await
do {
let cardDetailsResponse = try await gr4vy.cardDetails.get(request: request)
print("Card brand: \(cardDetailsResponse.scheme ?? "unknown")")
print("Card type: \(cardDetailsResponse.cardType ?? "unknown")")
print("Card ID: \(cardDetailsResponse.id)")
// Access optional required fields
if let requiredFields = cardDetailsResponse.requiredFields {
print("Required fields available")
if let address = requiredFields.address {
print("Address fields: city=\(address.city ?? false), postalCode=\(address.postalCode ?? false)")
}
}
} catch {
print("Error fetching card details: \(error)")
}
// Completion handler
gr4vy.cardDetails.get(request: request) { result in
switch result {
case .success(let cardDetailsResponse):
print("Card brand: \(cardDetailsResponse.scheme ?? "unknown")")
print("Card type: \(cardDetailsResponse.cardType ?? "unknown")")
print("Card ID: \(cardDetailsResponse.id)")
case .failure(let error):
print("Error fetching card details: \(error)")
}
}List all the stored payment methods for a buyer, filtered by the checkout's currency and country.
Note: Fields such as type and id in payment method items are optional and may be nil. Always use optional binding when accessing these fields.
// Create payment methods criteria
let paymentMethods = Gr4vyBuyersPaymentMethods(
buyerId: "buyer_123",
buyerExternalIdentifier: "external_456",
sortBy: .lastUsedAt,
orderBy: .desc,
country: "US",
currency: "USD"
)
// Create request
let request = Gr4vyBuyersPaymentMethodsRequest(
paymentMethods: paymentMethods,
merchantId: "merchant_123", // Optional
timeout: 30.0
)
// Async/await
do {
let paymentMethodsList = try await gr4vy.paymentMethods.list(request: request)
print("Found \(paymentMethodsList.count) payment methods")
} catch {
print("Error fetching payment methods: \(error)")
}
// Completion handler
gr4vy.paymentMethods.list(request: request) { result in
switch result {
case .success(let paymentMethodsList):
print("Found \(paymentMethodsList.count) payment methods")
case .failure(let error):
print("Error fetching payment methods: \(error)")
}
}The SDK provides support for 3D Secure (3DS) authentication, and handles flows automatically.
Authentication Flows:
- Frictionless Flow: Authentication completes in the background without user interaction
- Challenge Flow: User is presented with an authentication challenge (e.g., entering an OTP code)
Transaction Status Codes:
The authentication result includes a transaction status code that indicates the outcome.
You can customize the appearance of the 3DS challenge screen to match your app's design. The SDK supports separate customizations for light and dark modes.
Example: Basic Customization
// Create toolbar customization
let toolbar = Gr4vyThreeDSToolbarCustomization(
textColorHex: "#FFFFFF",
backgroundColorHex: "#007AFF",
headerText: "Secure Verification",
buttonText: "Cancel"
)
// Create button customizations
let submitButton = Gr4vyThreeDSButtonCustomization(
textFontSize: 16,
textColorHex: "#FFFFFF",
backgroundColorHex: "#007AFF",
cornerRadius: 8
)
let cancelButton = Gr4vyThreeDSButtonCustomization(
textFontSize: 16,
textColorHex: "#007AFF",
backgroundColorHex: "#F0F0F0",
cornerRadius: 8
)
// Create label customization
let label = Gr4vyThreeDSLabelCustomization(
textFontSize: 14,
textColorHex: "#333333",
headingTextFontSize: 18,
headingTextColorHex: "#000000"
)
// Create text box customization
let textBox = Gr4vyThreeDSTextBoxCustomization(
textFontSize: 16,
textColorHex: "#000000",
borderWidth: 1,
borderColorHex: "#CCCCCC",
cornerRadius: 4
)
// Combine into UI customization
let uiCustomization = Gr4vyThreeDSUiCustomization(
label: label,
toolbar: toolbar,
textBox: textBox,
buttons: [
.submit: submitButton,
.cancel: cancelButton
]
)
// Use with tokenization
let result = try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData,
authenticate: true,
uiCustomization: Gr4vyThreeDSUiCustomizationMap(default: uiCustomization)
)Example: Light and Dark Mode Support
// Light mode customization
let lightCustomization = Gr4vyThreeDSUiCustomization(
label: Gr4vyThreeDSLabelCustomization(
textColorHex: "#333333",
headingTextColorHex: "#000000"
),
toolbar: Gr4vyThreeDSToolbarCustomization(
textColorHex: "#000000",
backgroundColorHex: "#F8F8F8"
),
view: Gr4vyThreeDSViewCustomization(
challengeViewBackgroundColorHex: "#FFFFFF",
progressViewBackgroundColorHex: "#F0F0F0"
)
)
// Dark mode customization
let darkCustomization = Gr4vyThreeDSUiCustomization(
label: Gr4vyThreeDSLabelCustomization(
textColorHex: "#E0E0E0",
headingTextColorHex: "#FFFFFF"
),
toolbar: Gr4vyThreeDSToolbarCustomization(
textColorHex: "#FFFFFF",
backgroundColorHex: "#1C1C1E"
),
view: Gr4vyThreeDSViewCustomization(
challengeViewBackgroundColorHex: "#000000",
progressViewBackgroundColorHex: "#1C1C1E"
)
)
// Create customization map with both modes
let customizations = Gr4vyThreeDSUiCustomizationMap(
default: lightCustomization,
dark: darkCustomization
)
// Use with tokenization
let result = try await gr4vy.tokenize(
checkoutSessionId: "session_123",
cardData: cardData,
authenticate: true,
uiCustomization: customizations
)Available Customization Options:
- Toolbar: Header text, button text, colors, and fonts
- Labels: Body text and heading styles
- Buttons: Individual styling for different button types (submit, cancel, next, etc.)
- Text Boxes: Input field styling including borders and corner radius
- View: Background colors for the challenge and progress views
All color values should be provided as hexadecimal strings (e.g., "#007AFF").
By default, an API error will throw a Gr4vyError exception. The SDK provides error handling with specific error types. They are:
| Error Type | Description |
|---|---|
invalidGr4vyId |
Invalid Gr4vy ID provided |
badURL |
Invalid URL construction |
httpError |
HTTP request failed |
networkError |
Network connectivity issues |
decodingError |
JSON decoding failed |
threeDSError |
3D Secure authentication error |
uiContextError |
UI context resolution error |
import gr4vy_swift
do {
let paymentOptions = try await gr4vy.paymentOptions.list(request: request)
// Handle success
} catch let error as Gr4vyError {
switch error {
case .invalidGr4vyId:
print("Invalid Gr4vy ID provided")
case .badURL(let url):
print("Invalid URL: \(url)")
case .httpError(let statusCode, _, let message):
print("HTTP \(statusCode): \(message ?? "Unknown error")")
case .networkError(let urlError):
print("Network error: \(urlError.localizedDescription)")
case .decodingError(let message):
print("Decoding error: \(message)")
case .threeDSError(let message):
print("3DS authentication error: \(message)")
case .uiContextError(let message):
print("UI context error: \(message)")
}
} catch {
print("Unexpected error: \(error)")
}You can override the default server globally using the server parameter when initializing the SDK client instance. The selected server will then be used as the default for API calls to Gr4vy. Available configurations:
| Name | Server | Description |
|---|---|---|
sandbox |
https://api.sandbox.{id}.gr4vy.app |
Sandbox environment |
production |
https://api.{id}.gr4vy.app |
Production environment |
import gr4vy_swift
let gr4vy = try Gr4vy(
gr4vyId: "example",
token: "your_jwt_token",
merchantId: "default",
server: .production, // Use production environment
debugMode: false
)You can setup your SDK to emit debug logs for SDK requests and responses.
For request and response logging, enable debugMode when initializing the SDK:
let gr4vy = try Gr4vy(
gr4vyId: "example",
token: "your_jwt_token",
merchantId: "default",
server: .sandbox,
debugMode: true // Enable debug logging
)You can also manually control logging:
// Manually control logging
Gr4vyLogger.enable() // Enable logging
Gr4vyLogger.disable() // Disable loggingExample output:
[Gr4vy SDK] Network request: GET https://api.sandbox.example.gr4vy.app/payment-options
[Gr4vy SDK] Response: 200 OK
[Gr4vy SDK] Response time: 245ms
WARNING: This should only be used for temporary debugging purposes. Leaving this option on in a production system could expose credentials/secrets in logs. Authorization headers are automatically redacted.
- Documentation: https://docs.gr4vy.com
- Issues: GitHub Issues
- Email: mobile@gr4vy.com
This project is provided as-is under the LICENSE.