Skip to content

Conversation

@Sakshi146-eng
Copy link

@Sakshi146-eng Sakshi146-eng commented Oct 21, 2025

Klarna Payment Adapter Integration

This PR introduces a new payment adapter for Klarna, enabling global “Pay Later” and “Pay Now” transactions via the EasySwitch SDK.
Addresses issue #58


✨ What's New

  • New Adapter: KlarnaIntegrator following the EasySwitch BaseAdapter interface

  • Global Support: Integration with Klarna’s REST API covering EU, US, and APAC regions

  • Flexible Payment Options: Supports deferred, installment, and direct payment methods

  • HMAC Webhook Security: Validates all incoming webhook signatures securely

  • Comprehensive Unit Tests & Documentation: Ensures full coverage and integration clarity


🌍 Supported Regions & Currencies

Region | Countries | Example Currencies -- | -- | -- Europe | Sweden, Germany, UK, Finland, Norway | SEK, EUR, GBP North America | United States, Canada | USD, CAD Oceania | Australia, New Zealand | AUD, NZD

🔒 Webhook Security

  • Uses HMAC-SHA256 with the configured webhook_secret

  • Validates signature through validate_webhook()

  • Parses payload and normalizes status via parse_webhook()

  • Raises PaymentError for invalid or tampered webhooks


🏗️ Architecture

  • Inherits from BaseAdapter

  • Implements all core abstract methods:

    • send_payment()

    • check_status()

    • refund()

    • cancel_transaction()

    • get_transaction_detail()

  • Registered via @AdaptersRegistry.register("klarna")

  • Returns unified response types (TransactionResult, WebhookEvent)

  • Consistent exception handling through PaymentError


🧪 Tests

  • Unit Tests: tests/test_klarna.py

Coverage includes:

  • Credential validation

  • Header generation (Base64 Auth)

  • Webhook parsing and signature validation

  • Transaction formatting and sending

  • Error handling for failed requests

Run with:

pytest tests/test_klarna_integrator.py -v

🚫 Unsupported / Not Implemented

❌ Partial refund operations (currently under evaluation)
❌ Currency conversion between unsupported Klarna markets

Copy link
Collaborator

@Einswilli Einswilli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributoins!


def validate_credentials(self) -> bool:
"""Validate Klarna API credentials."""
return bool(self.config.api_key and getattr(self.config, "api_username", None))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think api_username must be in the extra attr.
self.config here is an instance of ProviderConfig so it'd be better to have api_username and other aggregator specific config attrs in extra...

"""Validate Klarna webhook signature (if provided)."""
# Klarna allows optional signature validation via HMAC
signature = headers.get("klarna-signature")
secret = getattr(self.config, "webhook_secret", None)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put webhook_secret in self.contiig.extra.

"locale": "en-SE",
"order_amount": int(transaction.amount * 100),
"order_tax_amount": 0,
"order_lines": [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plase can you explain this section to me??? :-)

}
],
"merchant_urls": {
"confirmation": transaction.callback_url or "https://example.com/confirm",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use transaction.callback_url or self.config.callback_url.

transaction_id=data.get("session_id"),
reference=transaction.reference,
provider=self.provider_name(),
status=TransactionStatus.PENDING.value,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need unnecessary .value here.

raw_response=data,
)

async def cancel_transaction(self, transaction_id: str) -> None:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancel_transaction must return a boolean.

@pytest.fixture
def klarna_config():
return {
"api_username": "test_user",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This goes to extra...

"api_username": "test_user",
"api_key": "test_key",
"environment": "sandbox",
"webhook_secret": "secret123",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you'll need to refractor tests based on the adapter changes...

@Sakshi146-eng
Copy link
Author

I have done the changes, please check once

@Sakshi146-eng
Copy link
Author

Sakshi146-eng commented Oct 25, 2025

Hey @Einswilli Can you please accept the pr, if the changes are correct?

@Einswilli
Copy link
Collaborator

Hi @Sakshi146-eng can you please provide a screenshot of the tests???

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants