Java SDK
The Papi Java SDK (papi-api-client) provides a typed client for the Papi payment API. It handles HTTP communication, serialization, and model classes so you can focus on your business logic.
Installation
The SDK is hosted on the Ibonia package repository. Add the repository and the dependency to your project.
Maven
<repositories>
<repository>
<id>ibonia-repo-group</id>
<url>https://package-repository.ibonia.com/repository/ibonia-mvn-group</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.ibonia.papi</groupId>
<artifactId>papi-api-client</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
Gradle
repositories {
maven { url 'https://package-repository.ibonia.com/repository/ibonia-mvn-group' }
}
dependencies {
implementation 'com.ibonia.papi:papi-api-client:1.0.2'
}
Core classes
| Class | Package | Description |
|---|---|---|
ApiClient | com.ibonia.papi.apiclient | Base HTTP client. Instantiate once and share. |
PaymentLinksApi | com.ibonia.papi.apiclient.api | Methods for creating payment links. |
PaymentLinkRequest | com.ibonia.papi.apiclient.model | Request body for creating a payment link. |
PaymentLinkResponse | com.ibonia.papi.apiclient.model | Response returned after a payment link is created. |
PaymentResponse | com.ibonia.papi.apiclient.model | Payload sent by Papi to your notification endpoint. |
Usage
Create one ApiClient instance (it is thread-safe) and pass it to PaymentLinksApi. In a Spring application, do this in the constructor so the API instance is ready when your bean is created.
import com.ibonia.papi.apiclient.ApiClient;
import com.ibonia.papi.apiclient.api.PaymentLinksApi;
ApiClient apiClient = new ApiClient();
PaymentLinksApi paymentLinksApi = new PaymentLinksApi(apiClient);
Construct a PaymentLinkRequest using the fluent builder. The fields amount, description, clientName, reference, and notificationUrl are required.
import com.ibonia.papi.apiclient.model.PaymentLinkRequest;
import java.net.URI;
PaymentLinkRequest request = new PaymentLinkRequest()
.amount(15000.0)
.description("Payment for Order #123")
.clientName("John Doe")
.reference("ORDER-123")
.payerEmail("john.doe@example.com")
.payerPhone("+261340000000")
.notificationUrl(new URI("https://yourapp.com/api/payment-notifications"))
.validDuration(60); // link expires after 60 minutes
Call createPaymentLink with your API key (from the dashboard) and the request. The method returns a wrapper — call .getData() to get the actual PaymentLinkResponse.
import com.ibonia.papi.apiclient.model.PaymentLinkResponse;
PaymentLinkResponse response = paymentLinksApi
.createPaymentLink("YOUR_API_KEY", request)
.getData();
String paymentUrl = response.getPaymentLink(); // redirect the user here
String notifToken = response.getNotificationToken(); // store this for verification
Redirect the customer to paymentUrl. After the payment, Papi will call your notificationUrl with the result.
Expose a POST endpoint that accepts a PaymentResponse body. Papi calls this endpoint after every payment attempt.
import com.ibonia.papi.apiclient.model.PaymentResponse;
@PostMapping("/api/payment-notifications")
public ResponseEntity<?> handleNotification(
@RequestBody @Valid PaymentResponse notification) {
// Verify authenticity before doing anything else
boolean tokenMatches = notifToken.equals(notification.getNotificationToken());
boolean refMatches = "ORDER-123".equals(notification.getMerchantPaymentReference());
if (!tokenMatches || !refMatches) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
switch (notification.getPaymentStatus()) {
case SUCCESS -> handleSuccess(notification);
case FAILED -> handleFailure(notification);
case PENDING -> handlePending(notification);
}
return ResponseEntity.ok().build();
}
PaymentLinkRequest fields
| Field | Type | Required | Description |
|---|---|---|---|
amount | double | ✓ | Payment amount (minimum 300 MGA). |
clientName | String | ✓ | Customer's full name. |
reference | String | ✓ | Your unique identifier for this payment. |
description | String | ✓ | Short payment description (max 255 chars). |
notificationUrl | URI | ✓ | Endpoint that receives payment status notifications. |
payerEmail | String | ✗ | Customer's email address. |
payerPhone | String | ✗ | Customer's phone number. |
successUrl | URI | ✗ | Redirect URL after a successful payment. |
failureUrl | URI | ✗ | Redirect URL after a failed payment. |
validDuration | int | ✗ | Link validity in minutes (default: 1). |
provider | String | ✗ | Lock to one provider: MVOLA, ARTEL_MONEY, ORANGE_MONEY, BRED. |
isTestMode | boolean | ✗ | true to flag the transaction as a test in the dashboard. |
testReason | String | ✗ | Reason displayed in the dashboard when test mode is on. |
PaymentResponse fields
| Field | Getter | Description |
|---|---|---|
paymentStatus | getPaymentStatus() | SUCCESS, FAILED, or PENDING. |
paymentMethod | getPaymentMethod() | Provider used: MVOLA, ARTEL_MONEY, ORANGE_MONEY, BRED. |
currency | getCurrency() | Always MGA. |
amount | getAmount() | Amount paid. |
fee | getFee() | Transaction fee deducted. |
clientName | getClientName() | Customer's name. |
description | getDescription() | Payment description. |
merchantPaymentReference | getMerchantPaymentReference() | Internal reference from the payment provider. |
notificationToken | getNotificationToken() | Token from the original payment link response — use to verify authenticity. |
paymentReference | getPaymentReference() | Your reference from the original request. |
payerEmail | getPayerEmail() | Customer email (if provided). |
payerPhone | getPayerPhone() | Customer phone (if provided). |
Full example (Spring Boot)
The example below shows a complete Spring Boot service that creates a payment link and handles the callback notification — the same pattern used in production.
import com.ibonia.papi.apiclient.ApiClient;
import com.ibonia.papi.apiclient.api.PaymentLinksApi;
import com.ibonia.papi.apiclient.model.PaymentLinkRequest;
import com.ibonia.papi.apiclient.model.PaymentLinkResponse;
import com.ibonia.papi.apiclient.model.PaymentResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.net.URI;
@Service
public class PaymentService {
private final PaymentLinksApi paymentLinksApi;
@Value("${app.domain}")
private String appDomain;
public PaymentService() {
this.paymentLinksApi = new PaymentLinksApi(new ApiClient());
}
public String createPaymentLink(String apiKey, double amount,
String reference, String customerName,
String email, String phone) throws Exception {
PaymentLinkRequest request = new PaymentLinkRequest()
.amount(amount)
.description("Payment " + reference)
.clientName(customerName)
.reference(reference)
.payerEmail(email)
.payerPhone(phone)
.notificationUrl(new URI(appDomain + "/api/payment-notifications"))
.validDuration(60);
PaymentLinkResponse response = paymentLinksApi
.createPaymentLink(apiKey, request)
.getData();
// Persist response.getNotificationToken() alongside the order reference
// so you can verify it when the notification arrives.
return response.getPaymentLink();
}
public void handleNotification(PaymentResponse notification) {
String storedToken = lookupNotificationToken(
notification.getMerchantPaymentReference());
if (!storedToken.equals(notification.getNotificationToken())) {
throw new SecurityException("Notification token mismatch");
}
if (notification.getPaymentStatus() == PaymentResponse.PaymentStatusEnum.SUCCESS) {
confirmOrder(notification.getMerchantPaymentReference());
}
}
// ... lookupNotificationToken and confirmOrder implementations
}