# Creating custom payment component

The integration gives you a raw version of the PaymentMollieProvider.vue component. You can use it as a base and customize it for your needs.

<template>
  <div class="payment-container">
    <slot name="loader" v-if="isLoadingPaymentMethods">
      <Loader />
    </slot>
    <slot name="error" v-else-if="isError">
      <div class="payment-error-container">
        {{ $t('Something went wrong. Please contact website\'s administrator.') }}
      </div>
    </slot>
    <slot name="no-payment-methods" v-else-if="!hasPaymentMethods">
      <div class="payment-warn-container">
        {{ $t('There are no available payment methods.') }}
      </div>
    </slot>
    <div v-else>
      <div class="payment-radio__container">
        <h4 class="payment-radio__title">{{ $t('Select payment method') }}</h4>
        <SfRadio
          v-e2e="'payment-method'"
          v-for="paymentMethod in paymentMethods"
          :key="paymentMethod.id"
          :label="paymentMethod.description"
          :value="paymentMethod.id"
          :selected ="selectedPaymentMethod && selectedPaymentMethod.id"
          name="paymentMethod"
          class="form__radio"
          @change="selectPaymentMethod(paymentMethod)"
        >
          <template #label>
            <div class="payment-radio__item">
              <img :src="paymentMethod.image.size1x" :alt="paymentMethod.description" /> <span class="payment-radio__item__text">{{ $t(paymentMethod.description) }}</span>
            </div>
          </template>
        </SfRadio>
      </div>

      <div class="payment-radio__container" v-if="selectedPaymentMethod && selectedPaymentMethod.issuers && selectedPaymentMethod.issuers.length">
        <h4 class="payment-radio__title">{{ $t('Select payment method\'s issuer') }}</h4>
        <SfRadio
          v-e2e="'payment-issuer'"
          v-for="paymentIssuer in selectedPaymentMethod.issuers"
          :key="paymentIssuer.id"
          :label="paymentIssuer.name"
          :value="paymentIssuer.id"
          :selected ="selectedIssuer && selectedIssuer.id"
          name="paymentIssuer"
          class="form__radio paymentIssuer"
          @change="selectIssuer(paymentIssuer)"
        >
          <template #label>
            <div class="payment-radio__item">
              <img :src="paymentIssuer.image.size1x" :alt="paymentIssuer.name" /> <span class="payment-radio__item__text">{{ $t(paymentIssuer.name) }}</span>
            </div>
          </template>
        </SfRadio>
      </div>

      <div class="payment-warn-container" v-if="selectedPaymentMethod && selectedPaymentMethod.issuers && !selectedPaymentMethod.issuers.length">
        {{ $t('Selected payment method requires issuer but doesn\'t give any option. Please select a different payment method, sorry.') }}
      </div>

      <SfButton 
        :disabled="loading || !canPlaceOrder"
        class="mt-10"
        @click="placeOrder"
      >{{ $t('Place order') }}</SfButton>
    </div>
  </div>
</template>

<script>
import { computed, onMounted, ref } from '@nuxtjs/composition-api';
import { useMollie } from '@vsf-enterprise/mollie-commercetools';
import Loader from '@vsf-enterprise/mollie-commercetools/src/components/PaymentStripeLoader.vue';
import { SfButton, SfRadio } from '@storefront-ui/vue';

export default {
  name: 'PaymentMollieProvider',
  components: {
    Loader,
    SfButton,
    SfRadio
  },
  props: {
    locale: {
      type: String,
      default: 'en_US'
    }
  },
  setup ({ locale }) {
    const isLoadingPaymentMethods = ref(true);
    const isError = ref(false);
    const paymentMethods = ref(null);

    const selectedPaymentMethod = ref(null);
    const selectPaymentMethod = paymentMethod => {
      selectedIssuer.value = null;
      selectedPaymentMethod.value = paymentMethod;
    };

    const selectedIssuer = ref(null);
    const selectIssuer = paymentIssuer => selectedIssuer.value = paymentIssuer;

    const canPlaceOrder = computed(() => {
      if (!selectedPaymentMethod.value?.id) {
        return false;
      }

      return selectedPaymentMethod.value.issuers 
        ? Boolean(selectedIssuer.value)
        : true;
    });

    const { 
      createContext,
      createOrder,
      error,
      loading
    } = useMollie();

    const loadPaymentMethods = async () => {
      const response = await createContext({
        locale
      });
      if (error.value.createContext) {
        isError.value = true;
        isLoadingPaymentMethods.value = false;
        return;
      }

      paymentMethods.value = response.methods;
      isLoadingPaymentMethods.value = false;
    };

    const placeOrder = async () => {
      const response = await createOrder({
        locale,
        paymentMethod: selectedPaymentMethod.value,
        issuer: selectedIssuer.value
      });

      if (error.value.createOrder) {
        isError.value = true;
        return;
      }

      const { checkoutUrl } = JSON.parse(response.interfaceInteractions[0].fields.response);
      window.location.href = checkoutUrl;
    };

    onMounted(async () => {
      if (!process.server) {
        await loadPaymentMethods();
      }
    });

    return {
      isError,
      isLoadingPaymentMethods,
      paymentMethods,
      hasPaymentMethods: computed(() => Boolean(paymentMethods.value?.length)),
      
      selectPaymentMethod,
      selectedPaymentMethod: computed(() => selectedPaymentMethod.value),
      selectedPaymentMethodRequiresIssuer: computed(() => Boolean(selectedPaymentMethod.value?.issuers?.length)),

      selectIssuer,
      selectedIssuer: computed(() => selectedIssuer.value),

      canPlaceOrder,
      placeOrder,
      loading
    };
  }
};
</script>

<style lang="scss" scoped>
  .mt-10 {
    margin-top: 10px;
  }
  
  .payment-container {
    margin: var(--spacer-xl) 0 0 0;
  }

  .payment-warn-container, .payment-error-container {
    border-radius: 5px;
    padding: 10px;
    margin-bottom: 10px;
  }

  .payment-warn-container {
    background: #ffcc00;
    color: #000;
  }

  .payment-error-container {
    background: #d12727;
    color: #fff;
  }

  .payment-radio__item {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;

    &__text {
      margin-left: 10px;
    }
  }
</style>