Cost+Docs

Java / Kotlin

SDK oficial de Kotlin/Java para la pasarela de pago Cost+

SDK oficial de Kotlin para la pasarela de pago Cost+, totalmente interoperable con Java. Simplifica el flujo de redireccion HPP (pagina de pago alojada), la firma de payloads HMAC y la verificacion de webhooks.

Caracteristicas

  • Kotlin-first, compatible con Java — data classes, coroutines, null safety; totalmente utilizable desde Java
  • kotlinx.serialization — sin dependencia de Gson/Jackson; mapeo automatico de snake_case/camelCase
  • Generacion de firma HMAC-SHA256 y verificacion en tiempo constante
  • Analisis de webhooks + verificacion de pedidos basada en API
  • Funciones suspend para IO sin bloqueo via coroutines de Kotlin
  • HttpClient inyectable para pruebas faciles con transportes mock
  • Compatible con Java 17+

Requisitos

Instalacion

Gradle (Kotlin DSL)

dependencies {
    implementation("io.nopayn:nopayn-sdk:1.0.0")
}

Gradle (Groovy)

dependencies {
    implementation 'io.nopayn:nopayn-sdk:1.0.0'
}

Maven

<dependency>
    <groupId>io.nopayn</groupId>
    <artifactId>nopayn-sdk</artifactId>
    <version>1.0.0</version>
</dependency>

Inicio rapido (Kotlin)

1. Inicializar el cliente

import io.nopayn.*
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    val nopayn = NoPaynClient(
        NoPaynConfig(
            apiKey = "your-api-key",
            merchantId = "your-project",
        )
    )
}

2. Crear un pago y redirigir al HPP

val result = nopayn.generatePaymentUrl(
    CreateOrderParams(
        amount = 1295,            // €12.95 in cents
        currency = "EUR",
        merchantOrderId = "ORDER-001",
        description = "Premium Widget",
        returnUrl = "https://shop.example.com/success",
        failureUrl = "https://shop.example.com/failure",
        webhookUrl = "https://shop.example.com/webhook",
        locale = "en-GB",
        expirationPeriod = "PT30M",
    )
)

println(result.orderUrl)    // HPP URL
println(result.paymentUrl)  // Direct payment method URL
println(result.signature)   // HMAC-SHA256 signature

3. Gestionar el webhook

// In your HTTP handler (Ktor, Spring, etc.)
val rawBody: String = request.body()
val verified = nopayn.verifyWebhook(rawBody)

println(verified.order.status)  // "completed", "cancelled", etc.
println(verified.isFinal)       // true when the order won't change

if (verified.order.status == "completed") {
    // Fulfil the order
}

Inicio rapido (Java)

import io.nopayn.*;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.Dispatchers;

public class Example {
    public static void main(String[] args) throws Exception {
        NoPaynClient client = new NoPaynClient(
            new NoPaynConfig("your-api-key", "your-project", "https://api.nopayn.co.uk")
        );

        Order order = BuildersKt.runBlocking(
            Dispatchers.getIO(),
            (scope, continuation) -> client.createOrder(
                new CreateOrderParams(
                    1295, "EUR", "ORDER-001", "Premium Widget",
                    "https://shop.example.com/success",
                    "https://shop.example.com/failure",
                    "https://shop.example.com/webhook",
                    "en-GB", null, "PT30M"
                ),
                continuation
            )
        );

        System.out.println("Order ID: " + order.getId());
        System.out.println("Order URL: " + order.getOrderUrl());

        // Signature utilities work synchronously
        String sig = client.generateSignature(1295, "EUR", order.getId());
        boolean valid = client.verifySignature(1295, "EUR", order.getId(), sig);
    }
}

Referencia de la API

NoPaynClient(config, httpClient?)

ParametroTipoObligatorioPredeterminado
config.apiKeyStringSi
config.merchantIdStringSi
config.baseUrlStringNohttps://api.nopayn.co.uk
httpClientjava.net.http.HttpClientNoCliente por defecto

client.createOrder(params): Order (suspend)

Crea un pedido via POST /v1/orders/.

ParametroTipoObligatorioDescripcion
amountIntSiImporte en la unidad monetaria mas pequena (centimos)
currencyStringSiCodigo ISO 4217 (EUR, GBP, USD, NOK, SEK)
merchantOrderIdString?NoTu referencia interna de pedido
descriptionString?NoDescripcion del pedido
returnUrlString?NoRedireccion despues de pago exitoso
failureUrlString?NoRedireccion en cancelacion/expiracion/error
webhookUrlString?NoNotificaciones asincronas de cambio de estado
localeString?NoIdioma del HPP (en-GB, de-DE, nl-NL, etc.)
paymentMethodsList<String>?NoFiltrar metodos del HPP
expirationPeriodString?NoDuracion ISO 8601 (PT30M)

Metodos de pago disponibles: credit-card, apple-pay, google-pay, vipps-mobilepay

client.getOrder(orderId): Order (suspend)

Obtiene los detalles del pedido via GET /v1/orders/{id}/.

client.createRefund(orderId, amount, description?): Refund (suspend)

Emite un reembolso total o parcial via POST /v1/orders/{id}/refunds/.

client.generatePaymentUrl(params): PaymentUrlResult (suspend)

Metodo de conveniencia que crea un pedido y devuelve:

PaymentUrlResult(
    orderId: String,     // NoPayn order UUID
    orderUrl: String,    // HPP URL
    paymentUrl: String?, // Direct payment URL (first transaction)
    signature: String,   // HMAC-SHA256 of amount:currency:orderId
    order: Order,        // Full order object
)

client.generateSignature(amount, currency, orderId): String

Genera una firma HMAC-SHA256 en hexadecimal. El mensaje canonico es $amount:$currency:$orderId, firmado con la clave API.

client.verifySignature(amount, currency, orderId, signature): Boolean

Verificacion en tiempo constante de una firma HMAC-SHA256.

client.verifyWebhook(rawBody): VerifiedWebhook (suspend)

Analiza el cuerpo del webhook, luego llama a GET /v1/orders/{id}/ para verificar el estado real.

Utilidades HMAC independientes

import io.nopayn.NoPaynSignature

val sig = NoPaynSignature.generate("your-api-key", 1295, "EUR", "order-uuid")
val ok  = NoPaynSignature.verify("your-api-key", 1295, "EUR", "order-uuid", sig)

Desde Java:

String sig = NoPaynSignature.generate("your-api-key", 1295, "EUR", "order-uuid");
boolean ok = NoPaynSignature.verify("your-api-key", 1295, "EUR", "order-uuid", sig);

Gestion de errores

import io.nopayn.*

try {
    nopayn.createOrder(CreateOrderParams(amount = 100, currency = "EUR"))
} catch (e: ApiException) {
    println(e.statusCode)  // 401, 400, etc.
    println(e.errorBody)   // Raw API error response
} catch (e: NoPaynException) {
    println(e.message)     // Network or parsing error
}

Estados del pedido

EstadoFinal?Descripcion
newNoPedido creado
processingNoPago en curso
completedSiPago exitoso — entregar los bienes
cancelledSiPago cancelado por el cliente
expiredSiEl enlace de pago expiro
errorSiFallo tecnico

Mejores practicas para webhooks

  1. Verifica siempre via la API — el payload del webhook solo contiene el ID del pedido, nunca el estado. El metodo verifyWebhook() del SDK hace esto automaticamente.
  2. Devuelve HTTP 200 para confirmar la recepcion. Cualquier otro codigo activa hasta 10 reintentos (cada 2 minutos).
  3. Implementa un poller de respaldo — para pedidos de mas de 10 minutos que no hayan alcanzado un estado final, consulta getOrder() como red de seguridad.
  4. Se idempotente — puedes recibir el mismo webhook multiples veces.

Tarjetas de prueba

Usa estas tarjetas en modo de prueba de Cost+ (sitio web sandbox):

TarjetaNumeroNotas
Visa (exito)4111 1111 1111 1111Cualquier CVV
Mastercard (exito)5544 3300 0003 7Cualquier CVV
Visa (rechazada)4111 1111 1111 1105Do Not Honor
Visa (fondos insuficientes)4111 1111 1111 1151Insufficient Funds

Usa cualquier fecha de caducidad futura y cualquier CVC de 3 digitos.

App de demostracion

Se incluye una app de demostracion basada en Docker (Ktor) en el repositorio de GitHub para probar el flujo completo de pago.

Soporte

Necesitas ayuda? Contacta a nuestro equipo de soporte en support@costplus.io.

On this page