Waffy Android Payment SDK
Native Android SDK for integrating Waffy payment services into your Android applications. Supports both Jetpack Compose and traditional Fragment implementations with WebView-based payment processing.
Key Features
PaymentComponent for modern Compose applications
PaymentFragment for traditional Android development
Full support for both Kotlin and Java development
Installation
Install the Waffy Android SDK from Maven Central or GitHub Packages
Option 1: Maven Central (Recommended)
Add to your app's build.gradle.kts:
dependencies {
implementation("io.github.waffyapp:waffy-sdk-android:0.5.113-alpha")
// For Jetpack Compose (if using PaymentComponent)
implementation(platform("androidx.compose:compose-bom:2024.02.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
// For Fragment support (if using PaymentFragment)
implementation("androidx.fragment:fragment-ktx:1.6.2")
implementation("androidx.appcompat:appcompat:1.6.1")
}Option 2: GitHub Packages (Requires Authentication)
First, create local.properties in your project root:
gpr.user=YOUR_GITHUB_USERNAME gpr.token=YOUR_GITHUB_TOKEN
Add to your project's settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://maven.pkg.github.com/WaffyApp/waffy-sdk-android")
credentials {
username = project.findProperty("gpr.user") as String?
password = project.findProperty("gpr.token") as String?
}
}
}
}Add to your app's build.gradle.kts:
dependencies {
implementation("com.waffy:waffy-sdk-android:0.5.113-alpha")
}Permissions
Add to your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Jetpack Compose Integration
Using PaymentComponent with Jetpack Compose (Recommended)
Use the PaymentComponent composable for seamless payment integration:
@Composable
fun BasicPaymentScreen(
paymentUrl: String,
userToken: String
) {
var currentStatus by remember { mutableStateOf("Initializing payment...") }
var isError by remember { mutableStateOf(false) }
// Create payment configuration
val paymentConfig = remember(paymentUrl, userToken) {
PaymentConfig.builder()
.url(paymentUrl)
.userToken(userToken)
.build()
}
Column(modifier = Modifier.fillMaxSize()) {
// Status Card
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = when {
isError -> MaterialTheme.colorScheme.errorContainer
else -> MaterialTheme.colorScheme.surfaceVariant
}
)
) {
Text(
text = currentStatus,
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.bodyLarge
)
}
// Payment Component
PaymentComponent(
paymentConfig = paymentConfig,
modifier = Modifier
.fillMaxSize()
.weight(1f),
onPaymentResult = { result ->
currentStatus = when (result) {
is PaymentResult.Success -> "✅ Payment completed successfully!"
is PaymentResult.Pending -> "⏳ Payment is being processed..."
is PaymentResult.Failed -> "❌ Payment failed. Please try again."
is PaymentResult.Canceled -> "🚫 Payment was canceled."
}
},
onPaymentError = { error, message ->
isError = true
currentStatus = when (error) {
PaymentError.NOT_CONFIGURED -> "⚠️ Payment setup error"
PaymentError.INVALID_URL -> "⚠️ Invalid payment link"
PaymentError.NETWORK -> "🌐 Connection problem"
PaymentError.UNKNOWN -> "❓ Unexpected error occurred"
}
}
)
}
}With Pre-styled Button
Use PayWithWaffyButton for consistent branding:
@Composable
fun CheckoutScreen() {
var showPayment by remember { mutableStateOf(false) }
if (!showPayment) {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Ready to complete your payment?",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(bottom = 24.dp)
)
PayWithWaffyButton(
onClick = { showPayment = true },
modifier = Modifier.fillMaxWidth()
)
}
} else {
BasicPaymentScreen(
paymentUrl = "https://payment.waffy.com/checkout/your-id",
userToken = "your_jwt_token"
)
}
}Fragment Integration
Using PaymentFragment for traditional Android development
Use PaymentFragment with navigation controls:
class PaymentActivity : AppCompatActivity(),
PaymentResultListener, PaymentErrorListener {
private lateinit var statusText: TextView
private lateinit var backButton: Button
private lateinit var reloadButton: Button
private var paymentFragment: PaymentFragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_payment)
initViews()
setupClickListeners()
loadPaymentFragment()
}
private fun loadPaymentFragment() {
val paymentUrl = intent.getStringExtra("PAYMENT_URL") ?: return
val userToken = intent.getStringExtra("USER_TOKEN") ?: return
val paymentConfig = PaymentConfig.builder()
.url(paymentUrl)
.userToken(userToken)
.build()
paymentFragment = PaymentFragment.newInstance(paymentConfig).apply {
setPaymentResultListener(this@PaymentActivity)
setPaymentErrorListener(this@PaymentActivity)
}
supportFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, paymentFragment!!)
.commit()
statusText.text = "Loading payment..."
}
private fun setupClickListeners() {
backButton.setOnClickListener {
if (paymentFragment?.canGoBack() == true) {
paymentFragment?.goBack()
} else {
finish()
}
}
reloadButton.setOnClickListener {
paymentFragment?.reload()
}
}
// PaymentResultListener implementation
override fun onPaymentResult(result: PaymentResult) {
runOnUiThread {
val message = when (result) {
is PaymentResult.Success -> "✅ Payment completed successfully!"
is PaymentResult.Pending -> "⏳ Payment is being processed..."
is PaymentResult.Failed -> "❌ Payment failed"
is PaymentResult.Canceled -> "🚫 Payment was canceled"
}
statusText.text = message
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
// PaymentErrorListener implementation
override fun onPaymentError(error: PaymentError, message: String) {
runOnUiThread {
val errorMessage = when (error) {
PaymentError.NOT_CONFIGURED -> "⚠️ Configuration Error: $message"
PaymentError.INVALID_URL -> "⚠️ Invalid URL: $message"
PaymentError.NETWORK -> "🌐 Network Error: $message"
PaymentError.UNKNOWN -> "❓ Unknown Error: $message"
}
statusText.text = errorMessage
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
}
}
}Payment Configuration
Configure payment parameters using the builder pattern
Use PaymentConfig.builder() to create configuration:
val paymentConfig = PaymentConfig.builder()
.url("https://payment.waffy.com/checkout/your-payment-id")
.userToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...")
.build()
// The SDK automatically appends userToken as a query parameter:
// Result URL: "https://payment.waffy.com/checkout/id?userTokenUrl=jwt_token"Payment URL Format
The payment URL should be the complete checkout URL with all required parameters from your backend. The SDK will automatically append the user token as a query parameter.
Payment Results & Error Handling
Handle various payment states and error scenarios
Payment Result Types
The SDK provides four distinct payment result states with redirect URLs:
Payment completed successfully
Payment is being processed (e.g., bank transfers)
Payment failed or was declined
User canceled or system timeout
Error Types
Handle different error scenarios appropriately:
override fun onPaymentError(error: PaymentError, message: String) {
when (error) {
PaymentError.NOT_CONFIGURED -> {
// Missing URL or token - configuration issue
Log.e("Payment", "Configuration error: $message")
showError("Payment setup error. Please contact support.")
}
PaymentError.INVALID_URL -> {
// Invalid payment URL format
Log.e("Payment", "Invalid URL: $message")
showError("Payment link is invalid.")
}
PaymentError.NETWORK -> {
// Network connectivity issues, HTTP errors
Log.e("Payment", "Network error: $message")
showRetryDialog("Network connection error. Please try again.")
}
PaymentError.UNKNOWN -> {
// Unexpected WebView or system errors
Log.e("Payment", "Unknown error: $message")
showError("An unexpected error occurred.")
}
}
}Testing & Verification
Verify your integration works correctly
Test SDK Installation
Create a simple test to verify the SDK is working:
import com.waffy.sdk.core.config.PaymentConfig
fun testSDK() {
try {
val config = PaymentConfig.builder()
.url("https://payment.waffy.com/test")
.userToken("test_token")
.build()
println("✅ SDK installed successfully!")
println("Config URL: ${config.buildCompleteUrl()}")
} catch (e: Exception) {
println("❌ SDK installation error: ${e.message}")
}
}Common Issues
Check your internet connection and verify the dependency version
Try invalidating caches and restarting Android Studio
Run ./gradlew clean build after adding dependencies
Complete Integration Guide
Step-by-step payment integration tutorial
API Reference
Detailed API documentation and parameters