implementation "group.kvell:kvell-java:1.2.0"import group.kvell.KvellClient;
KvellClient kvell = KvellClient.builder()
.apiKey(System.getenv("KVELL_API_KEY"))
.secretKey(System.getenv("KVELL_SECRET_KEY"))
.environment("sandbox") // switch to "production" when you go live
.build();
import group.kvell.KvellClient;
import group.kvell.model.CreateSessionParams;
import group.kvell.model.PaymentSession;
public class CreateCheckout {
public static void main(String[] args) {
KvellClient kvell = KvellClient.builder()
.apiKey(System.getenv("KVELL_API_KEY"))
.secretKey(System.getenv("KVELL_SECRET_KEY"))
.environment("sandbox")
.build();
// Amounts are always integers in kuruş (1/100 TRY) — 15000 = 150.00 TRY
PaymentSession session = kvell.paymentSessions().create(CreateSessionParams.builder()
.transactionId("ORDER-2026-001")
.amount(15000)
.currency("TRY")
.description("Order #1234")
.returnUrl("https://merchant.example.com/return")
.build());
System.out.println("Redirect the buyer to: " + session.getCheckoutUrl());
}
}
import group.kvell.KvellClient;
import group.kvell.KvellError;
import group.kvell.model.ChargeParams;
import group.kvell.model.Payment;
public class ChargeCard {
public static void main(String[] args) {
KvellClient kvell = KvellClient.builder()
.apiKey(System.getenv("KVELL_API_KEY"))
.secretKey(System.getenv("KVELL_SECRET_KEY"))
.environment("sandbox")
.build();
try {
Payment payment = kvell.payments().charge(ChargeParams.builder()
.transactionId("ORDER-2026-001")
.amount(15000) // kuruş
.currency("TRY")
.cardToken("tok_9f8e7d6c5b4a")
.build());
System.out.println("Captured payment " + payment.getPaymentId());
} catch (KvellError err) {
switch (err.getCode()) {
case "KVELL-PAY-4001":
System.err.println("Card declined (requestId=" + err.getRequestId() + ")");
break;
case "KVELL-GW-1002":
System.err.println("Signature invalid, check your secret key (requestId=" + err.getRequestId() + ")");
break;
default:
System.err.println("Kvell error " + err.getCode() + " (requestId=" + err.getRequestId() + ")");
}
throw err;
}
}
}
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@RestController
public class KvellWebhookController {
// swap for Redis/DB in production
private final Set<String> seenEventIds = ConcurrentHashMap.newKeySet();
@PostMapping(value = "/webhooks/kvell", consumes = "application/json")
public ResponseEntity<String> handle(@RequestBody byte[] rawBody,
@RequestHeader("X-Kvell-Signature") String signature,
@RequestHeader("X-Kvell-Event-Id") String eventId) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(System.getenv("KVELL_WEBHOOK_SECRET").getBytes(), "HmacSHA256"));
StringBuilder hex = new StringBuilder("sha256=");
for (byte b : mac.doFinal(rawBody)) hex.append(String.format("%02x", b & 0xff));
if (!MessageDigest.isEqual(signature.getBytes(), hex.toString().getBytes())) {
return ResponseEntity.status(401).body("invalid signature");
}
if (!seenEventIds.add(eventId)) {
return ResponseEntity.ok("duplicate");
}
String eventType = KvellEvent.parse(rawBody).getEventType();
switch (eventType) {
case "payment.captured":
// Fulfil the order asynchronously; respond fast below
break;
default:
break;
}
return ResponseEntity.ok("ok");
}
}