Integrate Braintree Dropin UI with Symfony UX Live Component

82 Views Asked by At

I have created a Symfony form using the UX Live Component.

<?php

// statements 

#[AsLiveComponent(
    name: "ChargeCardForm",
    template: "admin/components/charge-card-form.html.twig"
)]
class ChargeCardForm extends AbstractController
{
    use DefaultActionTrait;
    use LiveCollectionTrait;
    use ComponentWithFormTrait;

    protected function instantiateForm(): FormInterface
    {
        return $this->createForm(ChargeCardType::class);
    }

    #[LiveAction]
    public function save(): Response
    {
        $this->submitForm();
        $form = $this->getForm();
        $data = $form->getData();
        dd($data);
    }

}

where ChargeCardType::class is

<?php

// statements

class ChargeCardType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder->add('amount', Type\TextType::class, [
            'label' => '<b>Charge</b> Amount',
            'label_html' => true,
            'constraints' => [
                new Constraints\NotBlank(),
                new Constraints\Regex([
                    'pattern' => '/^\d+(\.\d{1,2})?$/',
                    'message' => 'Please enter a valid amount.',
                ]),
                new Constraints\GreaterThan(0),
            ]
        ]);


        $builder->add('paymentNonce', Type\HiddenType::class);

        $builder->add('internalNote', Type\TextareaType::class, [
            'label' => '<b>Internal</b> Note',
            'label_html' => true,
            'constraints' => [
                new Constraints\NotBlank(),
            ]
        ]);

        $builder->add('customerNote', Type\TextareaType::class, [
            'label' => '<b>Customer</b> Note',
            'label_html' => true,
        ]);

    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([]);
    }
}

and in the component template I have the followings


<div {{ attributes }}>
    {{ form_start(form, {attr: {
        'novalidate': true,
        'data-action': 'live#action',
        'data-action-name': 'prevent',
    }}) }}

    <div class="card shadow-none mb-2">
        <div class="card-body credit-card">
            <div id="dropin-container"></div>
        </div>
    </div>
    {{ form_widget(form) }}
    <div class="mb-3">
        <button type="submit" data-loading="action(save)|addAttribute(disabled)" class="btn btn-primary">
            <span data-loading="action(save)|show" style="display: none">Charging Card...</span>
            <span data-loading="action(save)|hide" style="display: block">Charge Card</span>
        </button>
    </div>

    {{ form_end(form) }}
    <script src="https://js.braintreegateway.com/web/dropin/1.40.2/js/dropin.min.js"></script>
    <script>
        const form = document.querySelector('[name="charge_card"]');

        braintree.dropin.create({
            authorization: 'token',
            container: '#dropin-container'
        }, (error, dropinInstance) => {
            if (error) console.error(error);

            form.addEventListener('submit', event => {
                event.preventDefault();

                dropinInstance.requestPaymentMethod((error, payload) => {
                    if (error) console.error(error);

                    // Step four: when the user is ready to complete their
                    //   transaction, use the dropinInstance to get a payment
                    //   method nonce for the user's selected payment method, then add
                    //   it a the hidden field before submitting the complete form to
                    //   a server-side integration
                    document.getElementById('charge_card_paymentNonce').value = payload.nonce;
                    // form.submit();
                });
            });
        });
    </script>
    <style>
        .credit-card .form-control {
            padding: 0;
            height: 43px
        }
    </style>
</div>

So when I submit the form it will create two requests one for the UX Component "Save" method and another with Braintree UI to generate payment_nonce.

But, I need to make these requests in sequence, basically, I need to generate payment_nonce first and store it into the hidden field and then start the UX Component "Save" method.

Currently, the payment_nonce is being generated properly but not getting into the UX Component "Save" method as the request was created before the value is set on the hidden field.

0

There are 0 best solutions below