Android SDK v0.5.113-alpha

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

Jetpack Compose

PaymentComponent for modern Compose applications

Fragment Support

PaymentFragment for traditional Android development

Kotlin & Java

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:

PaymentResult.Success(redirectUrl)

Payment completed successfully

PaymentResult.Pending(redirectUrl)

Payment is being processed (e.g., bank transfers)

PaymentResult.Failed(redirectUrl)

Payment failed or was declined

PaymentResult.Canceled(redirectUrl)

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(&quot;Network connection error. Please try again.&quot;)
        }
        PaymentError.UNKNOWN -> {
            // Unexpected WebView or system errors
            Log.e("Payment", "Unknown error: $message")
            showError(&quot;An unexpected error occurred.&quot;)
        }
    }
}

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

Error: "Could not resolve io.github.waffyapp:waffy-sdk-android"

Check your internet connection and verify the dependency version

Sync Issues

Try invalidating caches and restarting Android Studio

Build Success Tips

Run ./gradlew clean build after adding dependencies

Complete Integration Guide

Step-by-step payment integration tutorial

API Reference

Detailed API documentation and parameters