<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mohamed Jadla</title>
    <description>The latest articles on Forem by Mohamed Jadla (@jadlamed007007007).</description>
    <link>https://forem.com/jadlamed007007007</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3800388%2F038526d0-4326-41a4-93ac-81c2c6c87752.jpeg</url>
      <title>Forem: Mohamed Jadla</title>
      <link>https://forem.com/jadlamed007007007</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jadlamed007007007"/>
    <language>en</language>
    <item>
      <title>Payment Gateways as Anti-Corruption Layers: Applying Hexagonal Architecture in Real-World PHP</title>
      <dc:creator>Mohamed Jadla</dc:creator>
      <pubDate>Sun, 01 Mar 2026 21:06:25 +0000</pubDate>
      <link>https://forem.com/jadlamed007007007/payment-gateways-as-anti-corruption-layers-applying-hexagonal-architecture-in-real-world-php-4c27</link>
      <guid>https://forem.com/jadlamed007007007/payment-gateways-as-anti-corruption-layers-applying-hexagonal-architecture-in-real-world-php-4c27</guid>
      <description>&lt;p&gt;Payment Gateways as Anti-Corruption Layers&lt;/p&gt;

&lt;p&gt;Most PHP applications integrate payments directly using provider SDKs.&lt;/p&gt;

&lt;p&gt;Example:\Stripe\Stripe::setApiKey($key);&lt;br&gt;
\Stripe\PaymentIntent::create([...]);&lt;br&gt;
This works.&lt;/p&gt;

&lt;p&gt;Until it doesn’t.&lt;/p&gt;

&lt;p&gt;The real architectural question isn’t how to charge a card.&lt;/p&gt;

&lt;p&gt;It’s:&lt;/p&gt;

&lt;p&gt;Should your domain know that Stripe exists?&lt;br&gt;
The Hidden Coupling Problem&lt;/p&gt;

&lt;p&gt;When business logic calls Stripe SDK directly, your domain layer now depends on:&lt;/p&gt;

&lt;p&gt;Stripe’s request models&lt;/p&gt;

&lt;p&gt;Stripe’s response objects&lt;/p&gt;

&lt;p&gt;Stripe’s exception types&lt;/p&gt;

&lt;p&gt;Stripe’s lifecycle semantics&lt;/p&gt;

&lt;p&gt;This is infrastructure bleeding into your core domain.&lt;/p&gt;

&lt;p&gt;And that’s where architecture begins to matter.&lt;/p&gt;

&lt;p&gt;Hexagonal Architecture Perspective&lt;/p&gt;

&lt;p&gt;In Hexagonal Architecture (Ports &amp;amp; Adapters):&lt;/p&gt;

&lt;p&gt;The Domain sits at the center.&lt;/p&gt;

&lt;p&gt;Infrastructure lives outside.&lt;/p&gt;

&lt;p&gt;Communication happens through defined ports.&lt;/p&gt;

&lt;p&gt;So the real question becomes:&lt;/p&gt;

&lt;p&gt;Should Stripe be inside the hexagon?&lt;/p&gt;

&lt;p&gt;The answer is no.&lt;/p&gt;

&lt;p&gt;Stripe is infrastructure.&lt;/p&gt;

&lt;p&gt;Introducing a Payment Port&lt;/p&gt;

&lt;p&gt;Instead of calling Stripe directly, the domain defines a port:&lt;/p&gt;

&lt;p&gt;interface PaymentPort {&lt;br&gt;
    public function charge(Money $amount): PaymentResult;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The application layer depends only on this interface.&lt;/p&gt;

&lt;p&gt;No Stripe.&lt;br&gt;
No PayPal.&lt;br&gt;
No SDK types.&lt;/p&gt;

&lt;p&gt;Only domain language.&lt;/p&gt;

&lt;p&gt;Stripe as an Adapter Boundary&lt;/p&gt;

&lt;p&gt;Stripe now becomes an Adapter:&lt;/p&gt;

&lt;p&gt;Domain → PaymentPort → StripeAdapter → Stripe SDK&lt;/p&gt;

&lt;p&gt;The adapter:&lt;/p&gt;

&lt;p&gt;Translates domain objects into Stripe requests&lt;/p&gt;

&lt;p&gt;Maps Stripe responses into domain DTOs&lt;/p&gt;

&lt;p&gt;Converts SDK exceptions into domain exceptions&lt;/p&gt;

&lt;p&gt;This is the Adapter Boundary.&lt;/p&gt;

&lt;p&gt;Payment Layer as an Anti-Corruption Layer&lt;/p&gt;

&lt;p&gt;In Domain-Driven Design, an Anti-Corruption Layer (ACL) protects your domain from external models.&lt;/p&gt;

&lt;p&gt;Stripe has its own language:&lt;/p&gt;

&lt;p&gt;PaymentIntent&lt;/p&gt;

&lt;p&gt;SetupIntent&lt;/p&gt;

&lt;p&gt;CheckoutSession&lt;/p&gt;

&lt;p&gt;Your domain might speak:&lt;/p&gt;

&lt;p&gt;Charge&lt;/p&gt;

&lt;p&gt;Subscription&lt;/p&gt;

&lt;p&gt;Invoice&lt;/p&gt;

&lt;p&gt;Without an ACL, your domain starts speaking Stripe’s language.&lt;/p&gt;

&lt;p&gt;That’s architectural erosion.&lt;/p&gt;

&lt;p&gt;An abstraction layer acts as an Anti-Corruption Layer.&lt;/p&gt;

&lt;p&gt;It prevents provider vocabulary from leaking inward.&lt;/p&gt;

&lt;p&gt;Domain Isolation in Practice&lt;/p&gt;

&lt;p&gt;With proper isolation:&lt;/p&gt;

&lt;p&gt;✔ You can switch providers without touching business logic&lt;br&gt;
✔ You can mock the PaymentPort in tests&lt;br&gt;
✔ You can simulate failures cleanly&lt;br&gt;
✔ You maintain language consistency in the domain&lt;/p&gt;

&lt;p&gt;Your domain remains pure.&lt;/p&gt;

&lt;p&gt;But Let’s Be Honest&lt;/p&gt;

&lt;p&gt;This adds:&lt;/p&gt;

&lt;p&gt;More files&lt;/p&gt;

&lt;p&gt;More interfaces&lt;/p&gt;

&lt;p&gt;More mapping logic&lt;/p&gt;

&lt;p&gt;More maintenance cost&lt;/p&gt;

&lt;p&gt;If you only ever use one provider,&lt;br&gt;
this might be premature abstraction.&lt;/p&gt;

&lt;p&gt;Architecture is about trade-offs, not dogma.&lt;/p&gt;

&lt;p&gt;The Real Decision Framework&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;p&gt;Is the payment provider core to the business?&lt;/p&gt;

&lt;p&gt;Will we expand internationally?&lt;/p&gt;

&lt;p&gt;Do we expect regulatory constraints?&lt;/p&gt;

&lt;p&gt;Are we building a long-lived SaaS?&lt;/p&gt;

&lt;p&gt;If yes → abstraction is insurance.&lt;/p&gt;

&lt;p&gt;If not → direct SDK integration may be pragmatic.&lt;/p&gt;

&lt;p&gt;A Subtle Insight&lt;/p&gt;

&lt;p&gt;Payment gateways are not just APIs.&lt;/p&gt;

&lt;p&gt;They are external bounded contexts.&lt;/p&gt;

&lt;p&gt;Treating them as simple SDK calls ignores that reality.&lt;/p&gt;

&lt;p&gt;The moment billing logic becomes business-critical,&lt;br&gt;
you are no longer integrating an API —&lt;br&gt;
you are integrating another domain.&lt;/p&gt;

&lt;p&gt;That’s when Anti-Corruption Layers matter.&lt;/p&gt;

&lt;p&gt;Final Thought&lt;/p&gt;

&lt;p&gt;Architecture isn’t about adding layers.&lt;/p&gt;

&lt;p&gt;It’s about controlling dependency direction.&lt;/p&gt;

&lt;p&gt;The question isn’t:&lt;/p&gt;

&lt;p&gt;“How do we integrate Stripe?”&lt;/p&gt;

&lt;p&gt;The question is:&lt;/p&gt;

&lt;p&gt;“How do we prevent Stripe from owning our domain?”&lt;/p&gt;

&lt;p&gt;Curious how other senior engineers approach this.&lt;/p&gt;

&lt;p&gt;Do you abstract payment providers from day one?&lt;/p&gt;

&lt;p&gt;Or only after the second integration becomes real?&lt;/p&gt;

&lt;p&gt;👀 githup &lt;a href="https://github.com/jadlamed007007007/multi-payment" rel="noopener noreferrer"&gt;https://github.com/jadlamed007007007/multi-payment&lt;/a&gt; &lt;br&gt;
👀 packagist &lt;a href="https://packagist.org/packages/mohamedjadla/multi-payment" rel="noopener noreferrer"&gt;https://packagist.org/packages/mohamedjadla/multi-payment&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>opensource</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
