<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[figonzal.cl]]></title><description><![CDATA[figonzal.cl]]></description><link>https://blog.figonzal.cl</link><generator>RSS for Node</generator><lastBuildDate>Thu, 04 Jun 2026 23:05:49 GMT</lastBuildDate><atom:link href="https://blog.figonzal.cl/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Integrating Webpay Plus into a modern stack ]]></title><description><![CDATA[If you've ever worked on an e-commerce project in Chile, sooner or later you bump into Transbank. There's no avoiding it.
I built a reference template that use NestJS on the backend and Next.js 16 on ]]></description><link>https://blog.figonzal.cl/integrating-webpay-plus-into-a-modern-stack</link><guid isPermaLink="true">https://blog.figonzal.cl/integrating-webpay-plus-into-a-modern-stack</guid><category><![CDATA[webpay]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[transbank]]></category><category><![CDATA[webpayplus]]></category><category><![CDATA[webpay-plus]]></category><category><![CDATA[payments]]></category><category><![CDATA[integration]]></category><category><![CDATA[webdev]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[fintech]]></category><category><![CDATA[chile]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[React]]></category><category><![CDATA[api]]></category><category><![CDATA[backend]]></category><category><![CDATA[full stack]]></category><dc:creator><![CDATA[Felipe González]]></dc:creator><pubDate>Wed, 03 Jun 2026 21:42:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/63a1390a0422669e82b0e4cc/de29e7f4-ebfc-465e-bd2d-bef41bb65442.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've ever worked on an e-commerce project in Chile, sooner or later you bump into Transbank. There's no avoiding it.</p>
<p>I built a reference template that use NestJS on the backend and Next.js 16 on the frontend, and in this post I want to walk you through the whole thing: what <a href="https://transbankdevelopers.cl/documentacion/webpay-plus">Webpay Plus</a> actually is, why the integration looks the way it does, and how each piece fits together.</p>
<p><strong>Github Repository Reference:</strong> <a href="https://github.com/figonzal1/webpay-nestjs-nextjs">Link</a></p>
<hr />
<h2>A bit of context</h2>
<p><strong>Webpay Plus</strong> is one of the most important online payment methods in Chile. The user experience is straightforward: your customer enters an amount on your site, gets redirected to Transbank's branded payment page, fills in their card details there, and comes back to your site with a result.</p>
<p>From a UX perspective it's not as slick as Stripe Elements or a fully embedded checkout — the user always sees Transbank's domain in the address bar during the payment — but from a developer's perspective that's actually a feature.</p>
<p><em><strong>You never touch card numbers. PCI scope stays minimal. Transbank handles 3D Secure, fraud rules, and bank routing.</strong></em></p>
<p>The trade-off is that the integration model is what you might politely call <strong>classical</strong>. It's built around full-page redirects and form POSTs, not modern APIs with JSON responses and webhooks. That's important to understand up front, because it shapes every decision you'll make in the code.</p>
<hr />
<h1>The integration flow, step by step</h1>
<p>Before looking at any code, it helps to have a clear mental model of <strong>what's happening</strong>. There are <strong>three distinct moments</strong> where your system talks to Transbank's system, and each one has a specific shape.</p>
<ol>
<li><p><strong>First Step:</strong> When the customer clicks "Pay", the <strong>backend</strong> asks <strong>Transbank</strong> to create a <code>transaction (amount, order ID, return URL)</code>. Transbank returns a transaction token and a redirect URL where you must send the user.</p>
</li>
<li><p><strong>Second Step:</strong> Transbank requires the redirect to be an HTTP POST with the token in a <code>token_ws</code> form field, so you must generate an HTML form with a hidden <code>token_ws</code> input and submit it programmatically to avoid token leakage via history/referrers. Once on Transbank's page, the user enters their card details and either completes or cancels the payment. This step is entirely out of your hands — which, again, is the point.</p>
</li>
<li><p><strong>Third Step:</strong> When <strong>Transbank</strong> redirects back with the <code>token_ws</code>, your backend calls <strong>Transbank</strong> to validate the token and retrieve the transaction details (response_code <code>0</code> = approved); then you route the user to a success or error page.</p>
</li>
</ol>
<blockquote>
<p>Note: Transbank can POST the return (e.g., on timeout, cancel, or abandonment) to the same return URL instead of a GET, so make sure your backend accepts and handles both <code>POST</code> and <code>GET</code> to avoid broken pages.</p>
</blockquote>
<p><strong>Sequence diagram of the steps</strong></p>
<p><a href="https://mermaid.live/edit#pako:eNqNVGFP2zAQ_Ss3fwIptE1bCESCDwW0TUiA1lSTpkrITQ7ISOzubEOhqrQfsV-4X7KzQwst28Sn2Pfevbt7Omcucl2gSIXBHw5VjielvCFZjxWAdFYrV0-Q_G0qyZZ5OZXKwgikgXOc2dZ3s4kNPPYVJ1P5eKyVJV1VbwWGL6Qh0n2Z4yYj84yMpDITqe5geHLGDE861xZB3yPBKMpSOCaUHLCeKXNbauVJo52jo0EKlxfDDNoPoQ7MZa2dsguPDxgfppCH5Gy2tcS2PThkkJXtrNXgWxP3eEEFUgQGjeESn4sImowICK0jNaIq5GY7jfLc6jtUETiqFo1m09FmfODjozfxvwz6BYuSMLdg9bNzy0EZ_JRll82015pqeCjtLQTFqwezrsXkkeEvKotkIJdUQIFWlpX5h8FaXZesueFw1szz8TQDTWtGt3Nd16UNG1Rxu2WN2ln4_fMXKL3qis_czODsKrs4Oz335BczaDlqm9C4yraRSIcdwsogyImmRm-VHyb2VV4P_W7BpuGguGpvykz2aKnTrEvg8bosWdsN_GphAmMTXy0Fa061MnjlHx1vk5XWmUVD8l6t4XB4eAidBvzPKMblOW9lwwvjrKt8eJfKyhDWUEVwRhUiEjdUFiK15DASNVIt_VXMPWEs7C3WOBYpHwtJd2MxVgvO4ff7Tet6mUba3dyK9Fpyb5Fw04Kf1PNfZhUlroZ07F-USPtJEkREOhczkcadpLUbd3b7B0m30-_tJt1IPIp0Jz5IWp1ut9fb6-wlSW8vXkTiKdSNW3E_7vY5kbH93sF-f_EHAtigSw"><img src="https://mermaid.ink/img/pako:eNqNVGFP2zAQ_Ss3fwIptE1bCESCDwW0TUiA1lSTpkrITQ7ISOzubEOhqrQfsV-4X7KzQwst28Sn2Pfevbt7Omcucl2gSIXBHw5VjielvCFZjxWAdFYrV0-Q_G0qyZZ5OZXKwgikgXOc2dZ3s4kNPPYVJ1P5eKyVJV1VbwWGL6Qh0n2Z4yYj84yMpDITqe5geHLGDE861xZB3yPBKMpSOCaUHLCeKXNbauVJo52jo0EKlxfDDNoPoQ7MZa2dsguPDxgfppCH5Gy2tcS2PThkkJXtrNXgWxP3eEEFUgQGjeESn4sImowICK0jNaIq5GY7jfLc6jtUETiqFo1m09FmfODjozfxvwz6BYuSMLdg9bNzy0EZ_JRll82015pqeCjtLQTFqwezrsXkkeEvKotkIJdUQIFWlpX5h8FaXZesueFw1szz8TQDTWtGt3Nd16UNG1Rxu2WN2ln4_fMXKL3qis_czODsKrs4Oz335BczaDlqm9C4yraRSIcdwsogyImmRm-VHyb2VV4P_W7BpuGguGpvykz2aKnTrEvg8bosWdsN_GphAmMTXy0Fa061MnjlHx1vk5XWmUVD8l6t4XB4eAidBvzPKMblOW9lwwvjrKt8eJfKyhDWUEVwRhUiEjdUFiK15DASNVIt_VXMPWEs7C3WOBYpHwtJd2MxVgvO4ff7Tet6mUba3dyK9Fpyb5Fw04Kf1PNfZhUlroZ07F-USPtJEkREOhczkcadpLUbd3b7B0m30-_tJt1IPIp0Jz5IWp1ut9fb6-wlSW8vXkTiKdSNW3E_7vY5kbH93sF-f_EHAtigSw?type=png" alt="" style="display:block;margin:0 auto" /></a></p>
<hr />
<h2>Backend Code</h2>
<p>The controller stays thin — it reads the incoming parameters, decides which case it's in, and delegates the actual <strong>SDK</strong> call to the service. Here's the commit handler, which is where most of the interesting logic lives:</p>
<p><strong>Controller example</strong></p>
<pre><code class="language-typescript">private tx = WebpayPlus.Transaction.buildForIntegration(
    IntegrationCommerceCodes.WEBPAY_PLUS,
    IntegrationApiKeys.WEBPAY,
);

@All('/commit')
  async callback(@Req() req: Request, @Res() res: Response) {
    const params = req.method === 'GET' ? req.query : req.body;
    const tokenWs = params['token_ws'] as string;
    const tbkToken = params['TBK_TOKEN'] as string;
    const tbkOrdenCompra = params['TBK_ORDEN_COMPRA'] as string;
    const tbkIdSession = params['TBK_ID_SESSION'] as string;

    // Timeout
    if (!tokenWs &amp;&amp; !tbkToken) {
      this.logger.warn(
        `[\({req.method}] /webpay/commit timeout tbkOrdenCompra=\){tbkOrdenCompra} tbkIdSession=${tbkIdSession}`,
      );
      return res.redirect('http://localhost:4000/result/error');
    }

    // Abort
    if (tbkToken &amp;&amp; !tokenWs) {
      this.logger.warn(
        `[\({req.method}] /webpay/commit abort tbkToken=\){tbkToken} tbkOrdenCompra=${tbkOrdenCompra}`,
      );
      return res.redirect('http://localhost:4000/result/error');
    }

    // Commit TX
    this.logger.debug(`[\({req.method}] /webpay/commit token=\){tokenWs}`);
    const response = await this.webpayService.commitTx(tokenWs);

    if (response.response_code === 0) {
      this.logger.debug(`Commit successful, redirecting to success`);
      return res.redirect('http://localhost:4000/result/success');
    }

    this.logger.warn(`Commit failed responseCode=${response.response_code}, redirecting to error`);
    return res.redirect('http://localhost:4000/result/error');
  }
</code></pre>
<p>As you see, the <strong>Transbank SDK</strong> does most of the heavy lifting. As a example, here's what the "<em>create transaction</em>" call looks like in the <code>webpay.service.ts</code></p>
<pre><code class="language-typescript">async createTx(createWebpayDto: CreateWebpayDto) {

    //TODO: Change buyOrderId with your implementation
    const buyOrder = 'GT-' + Math.floor(Math.random() * 10000) + 1;

    //TODO: Change sessionId with your implementation
    const sessionId = 'Session-' + Math.floor(Math.random() * 10000) + 1;
    const returnUrl = 'http://localhost:3000/webpay/commit';

    this.logger.debug(
      `Creating transaction buyOrder=\({buyOrder} sessionId=\){sessionId} amount=${createWebpayDto.amount}`,
    );

    const response = await this.tx.create(buyOrder, sessionId, createWebpayDto.amount, returnUrl);

    this.logger.debug(`Transaction created token=\({response.token} url=\){response.url}`);

    return response;
  }
</code></pre>
<blockquote>
<p>Note: The IntegrationCommerceCodes and IntegrationApiKeys constants are public test credentials that Transbank uses as sandbox.</p>
<p>When you go to production, you replace these with your real credentials (which Transbank gives you after a commercial onboarding process) and switch Environment.Integration to Environment.Production.</p>
</blockquote>
<p>The <strong>returnUrl</strong> is the URL <strong>Transbank</strong> will send the user back to after the payment.</p>
<p><strong>This is critical: it has to be reachable from the user's browser (so no internal hostnames)</strong></p>
<p>And the path you specify is where the callback handler will live on your backend.</p>
<hr />
<h2>Frontend Code</h2>
<p>The trickiest part of the integration is step 2: Transbank requires a full-page HTTP POST with the token in a form field — a regular <code>fetch</code> or <code>router.push</code> won't work.</p>
<pre><code class="language-typescript">// PaymentForm.tsx
export function PaymentForm() {
  const [amount, setAmount] = useState("");
  const [tx, setTx] = useState&lt;{ token: string; url: string } | null&gt;(null);
  const [isPending, startTransition] = useTransition();
  const formRef = useRef&lt;HTMLFormElement&gt;(null);

  useEffect(() =&gt; {
    if (tx) formRef.current?.submit();
  }, [tx]);

  function handlePay() {
    startTransition(async () =&gt; {
      const result = await createTransaction(Number.parseInt(amount, 10));
      if ("error" in result) { setError(result.error); return; }
      setTx(result);
    });
  }

  return (
    &lt;&gt;
      {tx &amp;&amp; (
        &lt;form ref={formRef} method="post" action={tx.url} className="hidden"&gt;
          &lt;input type="hidden" name="token_ws" value={tx.token} /&gt;
        &lt;/form&gt;
      )}
      {/* amount input + pay button */}
    &lt;/&gt;
  );
}
</code></pre>
<p>The hidden form only enters the DOM once <strong>the token is ready</strong>. The useEffect watches for that and calls submit() immediately — the browser performs a full-page POST to Transbank's URL, and the user lands on their payment page.</p>
<p>Then we need a <strong>server action</strong> that keeps the backend URL off the client entirely:</p>
<pre><code class="language-typescript">'use server';
export async function createTransaction(amount: number) {
  const response = await fetch(`${process.env.API_URL}/webpay`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ amount }),
  });
  return response.json() as Promise&lt;{ token: string; url: string }&gt;;
}
</code></pre>
<p>The browser never sees <code>API_URL</code>. The action runs on the <strong>Next.js</strong> server, proxies the request to <strong>NestJS</strong> internally, and only <code>{ token, url }</code> reaches the client. You can change your backend URL without touching the client bundle.</p>
<h3><strong>Handling the result</strong></h3>
<p>When <strong>Transbank</strong> redirects the user back, the backend commits the transaction and redirects to one of two pages in <strong>Next.js</strong>: <code>/result/success</code> or <code>/result/error</code>. Both are simple static pages — success shows a confirmation, error covers rejections, cancellations, and timeouts alike.</p>
<hr />
<h3>Testing the whole thing</h3>
<p>You get a fully functional sandbox without having to sign anything or talk to anyone. They publish <a href="https://www.transbankdevelopers.cl/documentacion/como_empezar#tarjetas-de-prueba">test card numbers</a> you can use to simulate approved transactions, declined transactions, and various edge cases.</p>
<p>You can run the full flow end to end on your laptop, with no real money moving anywhere.</p>
<p><strong>To try the template:</strong></p>
<pre><code class="language-ssh">git clone https://github.com/figonzal/webpay-nestjs-nextjs 
cd webpay-nestjs-nextjs
</code></pre>
<ul>
<li>First, run the backend:</li>
</ul>
<pre><code class="language-typescript">cd webpay-nestjs &amp;&amp; pnpm install &amp;&amp; pnpm start:dev
</code></pre>
<ul>
<li>The, run the frontend:</li>
</ul>
<pre><code class="language-typescript">cd webpay-nextjs &amp;&amp; pnpm install &amp;&amp; pnpm dev
</code></pre>
<p>Open <code>http://localhost:4000</code>, type any amount, and walk through the full payment flow with one of the test cards. You'll see the redirect to <strong>Transbank</strong>, the payment form on their side, and the return to your success or error page.</p>
<hr />
<h2>Closing thoughts</h2>
<p>The <strong>Webpay Plus</strong> integration looks intimidating at first because the flow involves multiple redirects, two different HTTP verbs on the same endpoint, and a hidden form trick that feels out of place in <strong>2026</strong>. But once you see the three handshakes clearly — <em>create, redirect, commit</em> — the rest is mechanical.</p>
<p>A few things worth keeping in mind as you ship this: <strong>the commit endpoint needs to handle three distinct cases, not two</strong>. Transbank can return a <code>token_ws</code> (normal flow), a <code>TBK_TOKEN</code> without <code>token_ws</code> (user cancelled), or neither (timeout). The <strong>Server Action</strong> boundary is also worth preserving — proxying through Next.js keeps your NestJS URL off the client entirely, which pays off when you move from integration to production credentials.</p>
<p>The <strong>SDK</strong> does most of the work. Your job is to wire the pieces together correctly and handle the unhappy paths.</p>
<p>If this was useful, the full template is on <strong>GitHub</strong> — <em>Next.js frontend, NestJS backend, Transbank SDK</em> wired up in integration mode, and the full commit handler with all three cases covered. Clone it, run it with the test cards from Transbank's docs, and walk through the complete flow on your laptop before writing a single line of your own integration code. It's a much better starting point than a blank project.</p>
<hr />
<p>If you liked the article, feel free to give a thumbs up. You will motivate me to write more articles like this one. Thanks! ❤</p>
<p>¡Happy Coding!</p>
]]></content:encoded></item><item><title><![CDATA[OptionsMenu deprecated in Fragments ... Now what? - Android]]></title><description><![CDATA[Context
In version 1.4.0 of the androidx.activity:activity dependency, Google introduced the new MenuHost interfaces for activities to make it easier to create menus from any component.
But it was not until June 29, 2022, when Google released version...]]></description><link>https://blog.figonzal.cl/optionsmenu-deprecados-en-fragments-ahora-quc3a9-60dc20ead43a</link><guid isPermaLink="true">https://blog.figonzal.cl/optionsmenu-deprecados-en-fragments-ahora-quc3a9-60dc20ead43a</guid><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Developer]]></category><category><![CDATA[android apps]]></category><dc:creator><![CDATA[Felipe González]]></dc:creator><pubDate>Tue, 12 Jul 2022 18:07:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671574685324/t-M8Rg3_q.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-context">Context</h2>
<p>In version <strong><em>1.4.0</em></strong> of the <code>androidx.activity:activity</code> dependency, <strong>Google</strong> introduced the new <code>MenuHost</code> interfaces for activities to make it easier to create menus from any component.</p>
<p>But it was not until <strong><em>June 29, 2022,</em></strong> when <strong>Google</strong> released version <strong><em>1.5.0</em></strong> of the <code>androidx.fragment:fragment</code> dependency, where it has officially deprecated the old <code>onCreateOptionsMenu</code> &amp; <code>onOptionsItemSelected</code> override menu functions.</p>
<h2 id="heading-comparing-code">Comparing code</h2>
<h3 id="heading-before">Before</h3>
<p>Until before version 1.5.0, inflating a menu was done in the following way:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="240428d5afa5c82336668083b582c3ba"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/240428d5afa5c82336668083b582c3ba" class="embed-card">https://gist.github.com/figonzal1/240428d5afa5c82336668083b582c3ba</a></div><p> </p>
<p>On the other hand, the management of click events was done in this way:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="290bdc681551bca4389f1ed7f919e3bd"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/290bdc681551bca4389f1ed7f919e3bd" class="embed-card">https://gist.github.com/figonzal1/290bdc681551bca4389f1ed7f919e3bd</a></div><p> </p>
<p>This way had problems since the use of these fragment APIs generated a close dependency between the fragments and the containing activity, so they are not easy to test.</p>
<h3 id="heading-now">Now</h3>
<p><strong>Android</strong> provides a <code>MenuHost</code> and a <code>MenuProvider</code> , so menu creation and click handling are much simpler and no longer rely on the fragments menu APIs.</p>
<p>We only need to call the function <code>addMenuProvider</code>, which forces us to implement the functions to create a menu and the click event handler.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="70f465caed3ae7f7dd41bae4673d7cc1"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/70f465caed3ae7f7dd41bae4673d7cc1" class="embed-card">https://gist.github.com/figonzal1/70f465caed3ae7f7dd41bae4673d7cc1</a></div><p> </p>
<p>If you noticed, optionally, we can tell the <code>menuhost</code> that the menu is subject to the fragment's life cycle. With this parameter, you can tell the menu at what point in the fragment's life cycle it should be visible (in this case, the <code>resume</code> state).</p>
<p>This comes in handy if you have customized menus for each fragment. At the moment of changing the fragment, the menu will do it with it, respecting its life cycle automatically.</p>
<p>That's all for today ❤</p>
<p>If you liked the article, feel free to give a thumbs up. You will motivate me to write more articles like this one. Thanks!</p>
<h3 id="heading-references">References</h3>
<p>[Android Artifacts]: <a target="_blank" href="https://androidx.tech/artifacts/fragment/fragment-ktx/1.5.0">https://androidx.tech/artifacts/fragment/fragment-ktx/1.5.0</a></p>
<p>[ChangeLog —Activity]: <a target="_blank" href="https://developer.android.com/jetpack/androidx/releases/activity#version_140_3">https://developer.android.com/jetpack/androidx/releases/activity#version_140_3</a></p>
<p>[ChangeLog — Fragments]: <a target="_blank" href="https://developer.android.com/jetpack/androidx/releases/fragment#version_15_2">https://developer.android.com/jetpack/androidx/releases/fragment#version_15_2</a></p>
]]></content:encoded></item><item><title><![CDATA[Migration Guide: From Gradle Groovy to Kotlin DSL - Android]]></title><description><![CDATA[Android configuration files have used Gradle Groovy since the beginning, but in recent years with the appearance of Kotlin, these files can be written taking advantage of the full power of this language.
Therefore, Gradle has decided to support Kotli...]]></description><link>https://blog.figonzal.cl/migrate-gradle-kotlin-dsl-android</link><guid isPermaLink="true">https://blog.figonzal.cl/migrate-gradle-kotlin-dsl-android</guid><category><![CDATA[Kotlin]]></category><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[android apps]]></category><category><![CDATA[android development]]></category><dc:creator><![CDATA[Felipe González]]></dc:creator><pubDate>Mon, 20 Jun 2022 21:01:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1671574690164/UFtYoMKGM.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Android</strong> configuration files have used <strong>Gradle Groovy</strong> since the beginning, but in recent years with the appearance of <strong>Kotlin</strong>, these files can be written taking advantage of the full power of this language.</p>
<p>Therefore, <strong>Gradle</strong> has decided to support <strong>Kotlin</strong> for these configuration files using the <strong>Kotlin DSL</strong> variant.</p>
<p>Here are the changes you need to migrate from Groovy to Kotlin easily:</p>
<h2 id="heading-build-gradle-file-app">Build Gradle file (:app)</h2>
<h3 id="heading-adapt-dependencies-in-buildgradle">Adapt dependencies in build.gradle</h3>
<ul>
<li><p>Replace all single quotation marks (' ') by double quotation marks (" ")</p>
</li>
<li><p>You must indicate parentheses where there are function calls.</p>
</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="e53d9490700368ec45eb91be0d7d80ff"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/e53d9490700368ec45eb91be0d7d80ff" class="embed-card">https://gist.github.com/figonzal1/e53d9490700368ec45eb91be0d7d80ff</a></div><p> </p>
<h3 id="heading-adapt-plugins-inside-the-files">Adapt plugins inside the files</h3>
<ul>
<li>The plugins will now go inside a <code>plugins</code> function together with the <code>id</code> function</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="aedb5ae3646804c4d7f509b1b18fbf28"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/aedb5ae3646804c4d7f509b1b18fbf28" class="embed-card">https://gist.github.com/figonzal1/aedb5ae3646804c4d7f509b1b18fbf28</a></div><p> </p>
<h3 id="heading-build-types-configuration">Build types configuration</h3>
<ul>
<li><p>Now the <em>buildTypes</em> must be defined with the function <code>getByName</code></p>
</li>
<li><p>Verify that the property values are now assigned with an <code>=</code></p>
</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="ef568988ba0a2de0c6b2c597d83b07ac"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/ef568988ba0a2de0c6b2c597d83b07ac" class="embed-card">https://gist.github.com/figonzal1/ef568988ba0a2de0c6b2c597d83b07ac</a></div><p> </p>
<h3 id="heading-defaults-configs">Defaults Configs</h3>
<ul>
<li>Package configurations, SDK and version names have minor changes</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="15fe6d40d33220f2f9d0f3221aa1d5b9"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/15fe6d40d33220f2f9d0f3221aa1d5b9" class="embed-card">https://gist.github.com/figonzal1/15fe6d40d33220f2f9d0f3221aa1d5b9</a></div><p> </p>
<h2 id="heading-build-gradle-file-myapp">Build Gradle file (MyApp)</h2>
<h3 id="heading-adapt-classpath-dependencies">Adapt classpath dependencies</h3>
<ul>
<li>Again note double quotation marks and the use of parentheses.</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="053f722e3df25cf0f7cddf7d87b11dd1"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/053f722e3df25cf0f7cddf7d87b11dd1" class="embed-card">https://gist.github.com/figonzal1/053f722e3df25cf0f7cddf7d87b11dd1</a></div><p> </p>
<h3 id="heading-changes-in-task-clean-function">Changes in Task Clean function</h3>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="88aa881fd67bab57445fab2d5736ba7b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/88aa881fd67bab57445fab2d5736ba7b" class="embed-card">https://gist.github.com/figonzal1/88aa881fd67bab57445fab2d5736ba7b</a></div><p> </p>
<h3 id="heading-adapt-settingsgradle">Adapt Settings.gradle</h3>
<p>The settings.gradle file should be written in the following way</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="8d4e2051c6352aa5e55337329b3d8cfb"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/8d4e2051c6352aa5e55337329b3d8cfb" class="embed-card">https://gist.github.com/figonzal1/8d4e2051c6352aa5e55337329b3d8cfb</a></div><p> </p>
<h3 id="heading-finally-change-the-extension-in-all-files">Finally, change the extension in all files</h3>
<p>You must add to each file the extension <code>.kts</code> , the files would look like this:</p>
<ul>
<li><p>app/build.gradle.kts</p>
</li>
<li><p>build.gradle.kts</p>
</li>
<li><p>settings.gradle.kts</p>
</li>
</ul>
<p>It's as simple as that!</p>
<h2 id="heading-bonus-zone">BONUS ZONE</h2>
<h3 id="heading-load-information-from-keystore">Load information from <code>keystore</code></h3>
<p>To generate the <code>release</code> signature using the Keystore file you can load the variables in the following way</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="ac05b1c66fd406f51fa0add691630172"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/ac05b1c66fd406f51fa0add691630172" class="embed-card">https://gist.github.com/figonzal1/ac05b1c66fd406f51fa0add691630172</a></div><p> </p>
<h3 id="heading-load-variables-from-localproperties-and-exclude-library-within-a-dependency">Load variables from <code>local.properties</code> and exclude library within a dependency</h3>
<p>If you have any properties that you need to load from the local file or you need to exclude some library inside of a dependency, you can do it in the following way</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f6ddb3d46cb76969ce7161a2a2a442d8"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/figonzal1/f6ddb3d46cb76969ce7161a2a2a442d8" class="embed-card">https://gist.github.com/figonzal1/f6ddb3d46cb76969ce7161a2a2a442d8</a></div><p> </p>
<p>That's all for today, I hope your migration to Kotlin will be made easier with this article ❤.</p>
<p>If you liked the article, feel free to give a thumbs up. You will motivate me to write more articles like this one. Thanks!</p>
]]></content:encoded></item></channel></rss>