Skip to content

Instantly share code, notes, and snippets.

@Frank-Magmodules
Last active July 20, 2023 19:03
Show Gist options
  • Save Frank-Magmodules/ef3cb88ecbcd58b55ebe6769331583ed to your computer and use it in GitHub Desktop.
Save Frank-Magmodules/ef3cb88ecbcd58b55ebe6769331583ed to your computer and use it in GitHub Desktop.
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/vue@3"></script>
<script src="https://js.mollie.com/v1/mollie.js"></script>
<div class="mx-auto container mb-20">
<div id="app">
<div v-if="hasError" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
<span class="block sm:inline" v-text="errorMessage"></span>
</div>
<div class="border bg-gray-200 p-2 mb-4 mt-4">
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Profile ID (required)</dt>
<dd class="mt-1 text-sm text-gray-900"><input class="border px-2" type="text" v-model="profileId"></dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">
Mode
<span class="text-xs">(Must match <code>payment/mollie_general/type</code>)</span>
</dt>
<dd class="mt-1 text-sm text-gray-900">
<label><input type="radio" value="live" name="mode" v-model="mode"> Live</label><br>
<label><input type="radio" value="test" name="mode" v-model="mode"> Test</label>
</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">CurrentStep</dt>
<dd class="mt-1 text-sm text-gray-900">{{ currentStep }}</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">CartId</dt>
<dd class="mt-1 text-sm text-gray-900">{{ cartId }}</dd>
</div>
</dl>
</div>
<ul>
<li>
<svg v-if="!paused && !hasError && currentStep == 1" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 1" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Create cart
<span v-if="cartIdFromStorage">
(from storage)
</span>
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 2" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 2" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Add product to cart
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 3" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 3" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Add shipping & billing address
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 4" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 4" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Get shippings methods <span v-if="shippingMethod">({{ shippingMethod }})</span>
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 5" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 5" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Set shipping method
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 6" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 6" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Get payment methods
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 7" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 7" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Set the payment method
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 8" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 8" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Place order
</li>
<li>
<svg v-if="!paused && !hasError && currentStep == 9" class="animate-spin h-5 w-5 text-black inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
<svg v-if="!hasError && currentStep > 9" class="h-5 w-5 text-black inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"></path></svg>
Check transaction
</li>
</ul>
<div v-if="orderId">
<div class="rounded-md bg-green-50 p-4">
<div class="flex">
<div class="flex-shrink-0">
<!-- Heroicon name: solid/check-circle -->
<svg class="h-5 w-5 text-green-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-green-800">Order done!</h3>
<div class="mt-2 text-sm text-green-700">
<p><strong>Order ID:</strong> {{ orderId }}</p>
<p class="mt-4 font-bold">1. Please open this url and select a payment status (or not):</p>
<a v-bind:href="redirectUrl" target="_blank">{{ redirectUrl }}</a><br>
<p class="mt-4 font-bold">2. When you have opened the url, click here to get the payment status:</p>
<button @click="currentStep === 9 && checkTransactionStatus()" type="button" class="mt-4 inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Process Transaction
<svg v-if="checkingPaymentStatus" class="ml-2 animate-spin h-4 w-4 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
</button>
<div class="mt-4" v-if="paymentStatus">
<span class="font-bold">PaymentStatus</span>: {{ paymentStatus }}<br>
<span class="font-bold">redirect_to_cart</span>: {{ redirectToCart ? 'true' : 'false' }}<br>
<span class="font-bold">redirect_to_success_page</span>: {{ redirectToSuccessPage ? 'true' : 'false' }}<br>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="mollieMethods && currentStep === 6" class="grid gap-x-8 gap-y-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 mt-8">
<div
v-for="method in mollieMethods"
@click="setMethod(method.code)"
v-bind:class="{'bg-gray-300': selectedMethod === method.code}"
class="bg-gray-200 hover:bg-gray-300 cursor-pointer p-2"
>
<img v-bind:src="method.mollie_meta.image" />
{{ method.title }}
<span v-if="selectedMethod === method.code && method.mollie_available_issuers">
<ul>
<li v-for="issuer in method.mollie_available_issuers">
<label @click="setIssuer(issuer.code)">
<input type="radio" :checked="selectedIssuer === issuer.code" />
{{ issuer.name }}
</label>
</li>
</ul>
</span>
<div
v-if="method.code === 'mollie_methods_creditcard'"
v-show="selectedMethod === method.code"
class="pt-4"
>
<div class="mb-4">
<div class="text-small text-gray-600">Card Number</div>
<div class="bg-white py-2 px-2" ref="cardNumber"></div>
<div
class="p-2 mt-2 bg-red-300 border border-red-400 text-red-800"
ref="cardNumberError"
v-show="cardNumberError"
></div>
</div>
<div class="mb-4">
<div class="text-small text-gray-600">Card Holder</div>
<div class="bg-white py-2 px-2" ref="cardHolder"></div>
<div
class="p-2 mt-2 bg-red-300 border border-red-400 text-red-800"
ref="cardHolderError"
v-show="cardHolderError"
></div>
</div>
<div class="mb-4">
<div class="text-small text-gray-600">Expiry Date</div>
<div class="bg-white py-2 px-2" ref="expiryDate"></div>
<div
class="p-2 mt-2 bg-red-300 border border-red-400 text-red-800"
ref="expiryDateError"
v-show="expiryDateError"
></div>
</div>
<div class="mb-4">
<div class="text-small text-gray-600">CVV</div>
<div class="bg-white py-2 px-2" ref="verificationCode"></div>
<div
class="p-2 mt-2 bg-red-300 border border-red-400 text-red-800"
ref="verificationCodeError"
v-show="verificationCodeError"
></div>
</div>
</div>
</div>
</div>
<div v-if="paused && currentStep === 6 && selectedMethod" class="mt-8">
<div>
<button @click="callPlaceOrder()" type="button" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Place order</button>
</div>
</div>
<div v-if="paused && !hasError && currentStep === 8" class="mt-8">
<div>
<button @click="currentStep === 8 && callNextStep()" type="button" class="inline-flex items-center px-2.5 py-1.5 border border-transparent text-xs font-medium rounded shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Place order</button>
</div>
Continue!?
</div>
</div>
</div>
<script>
async function graphql(query, variables) {
const data = JSON.stringify({
query,
variables,
});
const response = await fetch(
'/graphql',
{
credentials: "same-origin",
method: 'post',
body: data,
headers: {
'Content-Type': 'application/json',
'Content-Length': data.length,
},
}
);
return await response.json();
}
</script>
<script>
const { createApp } = Vue
createApp({
data() {
return {
mode: 'test',
cartId: null,
cartIdFromStorage: false,
currentStep: 0,
orderId: null,
paused: false,
hasError: false,
errorMessage: '',
shippingMethod: '',
mollieMethods: [],
selectedMethod: null,
selectedIssuer: null,
redirectUrl: null,
paymentToken: null,
mollie: null,
fieldsMounted: false,
cardNumberError: false,
cardHolderError: false,
expiryDateError: false,
verificationCodeError: false,
componentsToken: null,
profileId: null,
checkingPaymentStatus: false,
paymentStatus: null,
redirectToCart: null,
redirectToSuccessPage: null,
}
},
async mounted() {
this.getProfileIdFromLocalStorage();
},
watch: {
selectedMethod(method) {
if (method !== 'mollie_methods_creditcard' || this.fieldsMounted) {
return;
}
this.addComponentField('cardNumber');
this.addComponentField('cardHolder');
this.addComponentField('expiryDate');
this.addComponentField('verificationCode');
this.fieldsMounted = true;
console.info('Mollie Component fields mounted');
},
profileId(profileId) {
if (!profileId) {
return;
}
localStorage.setItem('mollie_profile_id', profileId);
this.loadMollieComponents();
this.callNextStep();
}
},
methods: {
loadMollieComponents() {
this.mollie = Mollie(this.profileId, { locale: 'nl_NL', testmode: this.mode == 'test' });
},
getProfileIdFromLocalStorage() {
const profileId = localStorage.getItem('mollie_profile_id');
if (profileId) {
this.profileId = profileId;
this.loadMollieComponents();
}
},
async callPlaceOrder() {
if (this.selectedMethod !== 'mollie_methods_creditcard') {
this.currentStep === 6;
this.callNextStep();
return;
}
var { token, error } = await this.mollie.createToken();
if (error) {
// Something wrong happened while creating the token. Handle this situation gracefully.
this.hasError = 'One or more fields are invalid.';
this.errorMessage = error.message;
this.componentsToken = null;
return;
}
this.componentsToken = token;
this.currentStep === 6;
this.callNextStep();
},
addComponentField(name) {
const field = this.mollie.createComponent(name);
field.mount(this.$refs[name][0]);
field.addEventListener('change', event => {
if (event.error && event.touched) {
this.$refs[`${name}Error`][0].textContent = event.error;
this[`${name}Error`] = true
} else {
this.$refs[`${name}Error`][0].textContent = '';
this[`${name}Error`] = false
}
});
},
setMethod(method) {
this.selectedMethod = method;
},
setIssuer(issuer) {
this.selectedIssuer = issuer;
},
async callNextStep() {
if (this.hasError) {
return;
}
this.paused = false;
if (this.currentStep === 0) {
await this.getCart();
}
if (this.currentStep === 1) {
await this.addProductToCart();
}
if (this.currentStep === 2) {
await this.setShippingAndBillingAddress();
}
if (this.currentStep === 3) {
await this.getShippingMethods();
}
if (this.currentStep === 4) {
await this.setShippingMethodOnCart();
}
if (this.currentStep === 5) {
await this.getPaymentMethods();
}
if (this.currentStep === 6) {
await this.setPaymentMethodOnCart();
}
if (this.currentStep === 7) {
await this.setGuestEmailOnCart();
}
if (this.currentStep === 8) {
await this.placeOrder();
}
// 0. Create cart
// 1. Add product to cart
// 2. Add shipping & billing address
// 3. Get shippings methods
// 4. Set shipping method
// 5. Get payment methods
// 6. Set the payment method
// 7. Set guest email
// 8. Place order
// 9. Check transaction
if (this.currentStep === 9) {
await this.checkTransactionStatus();
}
this.currentStep++;
},
async getCart() {
const cartId = localStorage.getItem('cartId');
if (!cartId) {
this.createEmptyCart();
return;
}
graphql(
`
query getCart($cartId: String!) {
cart(cart_id: $cartId) {
id
}
}
`,
{
cartId,
}
).then(response => {
if (!response.data.cart) {
this.createEmptyCart();
return;
}
this.cartId = cartId;
this.cartIdFromStorage = true;
this.currentStep++;
this.callNextStep();
});
},
async createEmptyCart() {
graphql(`
mutation {
createEmptyCart
}
`).then(response => {
this.cartId = response.data.createEmptyCart;
this.callNextStep();
localStorage.setItem('cartId', this.cartId);
});
},
async addProductToCart() {
if (this.cartIdFromStorage) {
this.currentStep++;
this.callNextStep();
return;
}
graphql(`
mutation addSimpleProductsToCart($cartId: String!, $sku: String!) {
addSimpleProductsToCart(
input: {
cart_id: $cartId,
cart_items: [
{
data: {
quantity: 1
sku: $sku
}
}
]
}
) {
cart {
items {
id
product {
sku
stock_status
}
quantity
}
}
}
}
`,
{
cartId: this.cartId,
sku: '24-WB02',
}
).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage || response.errors[0].message;
throw new Error(response.errors[0].debugMessage || response.errors[0].message);
}
this.callNextStep();
});
},
async setShippingAndBillingAddress() {
graphql(`
mutation setBillingAddressOnCart($cartId: String!) {
setBillingAddressOnCart(
input: {
cart_id: $cartId
billing_address: {
address: {
firstname: "Michiel"
lastname: "Gerritsen"
company: "Control Alt Delete"
street: ["Hoofdweg", "95A"]
city: "De Cocksdorp"
postcode: "1795JC"
country_code: "NL"
telephone: "123-456-0000"
save_in_address_book: false
}
use_for_shipping: true
}
}
) {
cart {
billing_address {
firstname
lastname
company
street
city
postcode
telephone
country {
code
label
}
}
shipping_addresses {
firstname
lastname
company
street
city
postcode
telephone
country {
code
label
}
}
}
}
}
`,
{
cartId: this.cartId,
}
).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
this.callNextStep();
});
},
async getShippingMethods() {
graphql(`
query($cartId: String!) {
cart(cart_id: $cartId) {
shipping_addresses {
available_shipping_methods {
error_message
method_code
method_title
}
}
}
}
`,
{
cartId: this.cartId
}).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
const method = response.data.cart.shipping_addresses[0].available_shipping_methods[0];
this.shippingMethod = method.method_code;
this.callNextStep();
});
},
async setShippingMethodOnCart() {
graphql(`
mutation setShippingMethodsOnCart($cartId: String! $method: String!) {
setShippingMethodsOnCart(input: {
cart_id: $cartId
shipping_methods: [
{
carrier_code: $method
method_code: $method
}
]
}) {
cart {
shipping_addresses {
selected_shipping_method {
carrier_code
method_code
carrier_title
method_title
}
}
}
}
}
`,
{
cartId: this.cartId,
method: this.shippingMethod,
}
).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage || response.errors[0].message;
throw new Error(response.errors[0].debugMessage);
}
this.callNextStep();
});
},
async setGuestEmailOnCart() {
graphql(`
mutation setGuestEmailOnCart($cartId: String!) {
setGuestEmailOnCart(input: {
cart_id: $cartId
email: "guest@example.com"
}) {
cart {
email
}
}
}
`,
{
cartId: this.cartId,
}
).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
this.callNextStep();
});
},
async getPaymentMethods() {
graphql(`
query($cartId: String!) {
cart(cart_id: $cartId) {
available_payment_methods {
code
title
mollie_meta {
image
}
mollie_available_issuers {
name
code
image
svg
}
}
}
}
`, {
cartId: this.cartId,
}).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
this.paused = true;
const methods = response.data.cart.available_payment_methods;
this.mollieMethods = methods.filter(element => element.code.indexOf('mollie_methods_') === 0);
if (!this.mollieMethods) {
alert('No Mollie payment method found. Please check if they are activated');
return;
}
});
},
async setPaymentMethodOnCart() {
graphql(`
mutation setPaymentMethodOnCart(
$cartId: String!
$method: String!
$issuer: String
$componentsToken: String
) {
setPaymentMethodOnCart(input: {
cart_id: $cartId
payment_method: {
code: $method
mollie_card_token: $componentsToken
mollie_selected_issuer: $issuer
}
}) {
cart {
selected_payment_method {
code
}
}
}
}
`,
{
cartId: this.cartId,
method: this.selectedMethod,
issuer: this.selectedIssuer,
componentsToken: this.componentsToken,
}
).then(response => {
console.log('setPaymentMethodOnCart', response);
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage || response.errors[0].message;
throw new Error(response.errors[0].debugMessage);
}
this.callNextStep();
});
},
async placeOrder() {
graphql(`
mutation placeOrder($cartId: String!) {
placeOrder(input: {
cart_id: $cartId
}) {
order {
order_id
mollie_redirect_url
mollie_payment_token
}
}
}
`,
{
cartId: this.cartId,
}
).then(response => {
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
this.orderId = response.data.placeOrder.order.order_id;
this.redirectUrl = response.data.placeOrder.order.mollie_redirect_url
this.paymentToken = response.data.placeOrder.order.mollie_payment_token
this.paused = true;
})
},
async checkTransactionStatus() {
this.checkingPaymentStatus = true;
graphql(`
mutation($paymentToken: String!) {
mollieProcessTransaction(input: {
payment_token: $paymentToken
}) {
paymentStatus
redirect_to_cart
redirect_to_success_page
}
}
`, {
paymentToken: this.paymentToken
}).then(response => {
this.checkingPaymentStatus = false;
if (response.errors) {
this.hasError = true;
this.errorMessage = response.errors[0].debugMessage;
throw new Error(response.errors[0].debugMessage);
}
const data = response.data.mollieProcessTransaction;
this.paymentStatus = data.paymentStatus;
this.redirectToCart = data.redirect_to_cart;
this.redirectToSuccessPage = data.redirect_to_success_page;
this.paused = true;
})
}
}
}).mount('#app')
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment