Server http redirect to a third party payment url

Hi all,

I am trying to create a mollie.com payment inside a meteor method. The mollie api works fine and I managed to create payments. The problem is that I cannot figure out how to do a redirect to the payment url/page that is being returned by the mollie api.

According to the official node npm package I should use:

response.writeHead(302, { Location: payment.getPaymentUrl() });

This is what I have till now, I added a comment where I want the redirect to be.

mollie.payments.create({
    amount: "10.00",
    description: "Short description of payment",
    redirectUrl: Meteor.absoluteUrl() + "url/where/mollie/redirects/to",
    webhookUrl: Meteor.absoluteUrl() + "api/mollie/payment/" + paymentId,
    metadata: {
        order_id: paymentId
    }
}, Meteor.bindEnvironment(function(payment) {
    //console.log(payment);
    // do something with the payment object
    // TODO: contains the paymentUrl to redirect to for the user to start the payment
}));

How can this be accomplished in Meteor?

Thanks a lot!

Hi, Did you accomplish this?

Are you calling the method from the client side? If so just pass the payment URL back to client when the payment has completed, and handle the redirect on the client side. Here’s a really quick example (runnable app here) - replace the getRedirectUrl method with your real method:

example.html:

<body>
  <h1>Method Redirect</h1>
  <button>Redirect Me!</button>
</body>

example.js:

if (Meteor.isClient) {
  Template.body.events({
    'click button': (event) => {
      $(event.currentTarget).html('Redirecting ...');
      Meteor.call('getRedirectUrl', (error, redirectUrl) => {
        window.location.replace(redirectUrl);
      });
    }
  });
}

Meteor.methods({
  getRedirectUrl() {
    if (!this.isSimulation) {
      Meteor._sleepForMs(2000); // Adding in fake delay ...
      return 'https://meteor.com';
    }
  }
})
1 Like

Thanks for the answer, that’s getting pretty close. I call the method on both server and client. The mollie payment block is called within a if (!this.isSimulation) {};, so server only.
The only problem I still have:
The payment object lives in the callback:

Meteor.bindEnvironment(function(payment) {
    // url is within the payment object
}));

And I can’t find any way to return that value as the main method’s return value. It only only lives within that scope?
I tried to assign it to a variable outside that scope, but without success:

var paymentUrl = ''; // var outside scope
mollie.payments.create({
    amount: "10.00",
    description: "Short description of payment",
    redirectUrl: Meteor.absoluteUrl() + "url/where/mollie/redirects/to",
    webhookUrl: Meteor.absoluteUrl() + "api/mollie/payment/" + paymentId,
    metadata: {
        order_id: paymentId
    }
}, Meteor.bindEnvironment(function(payment) {
    paymentUrl = payment.getPaymentUrl();
}));
return paymentUrl; //returns nothing -> ''

I guess I am forgetting some basic JS concept?

I’m facing the same issues. Actually I managed to do this on server side and I was looking for a solution on the client side to check which is better.

I added the picker package () and then on client I’m having lets say a link

 <a href="/mollie/hkahsfe123/10"> Pay 10 euros</a>

and then on server side

// payment redirect route

Picker.route('/mollie/:orderId/:amount', function(params, request, response, next) {

    mollie.payments.create({
        amount: params.amount,
        description: 'My first TEST payment',
        redirectUrl: Meteor.absoluteUrl('payment/' + params.orderId),
        webhookUrl: Meteor.absoluteUrl('webhooks/mollie'),
        locale: 'en',
        metadata: {
            orderId: params.orderId
        }
    }, function(payment) {
        if (payment.error) {
            console.error(payment.error);
            response.end();
        }
        response.writeHead(302, {
            Location: payment.getPaymentUrl()
        });
        response.end();
    });
});

// webhooks route

Picker.route('/webhooks/mollie', function(params, request, response, next) {

    let id = request.body.id;

    console.log('Hooks', request.body);

    /*
      Retrieve the payment's current state.
     */
    mollie.payments.get(id, function(payment) {

        console.log(payment);

        if (payment.error) {
            console.error(payment.error);
            return response.end();
        }

        /*
          Update the order in the database.
         */

        if (payment.isPaid()) {

            /*
              At this point you'd probably want to start the process of delivering the product to the customer.
             */
        }
        else if (!payment.isOpen()) {

            /*
              The payment isn't paid and isn't open anymore. We can assume it was aborted.
             */
        }
    });

    response.statusCode = 200;
    response.end('OK');
});

I’m not sure is the best way to go, but couldn’;t find any proper documentation on this as I’m also trying to split payments.

I’m wrapping up with the oauth and accounts packages and will post them also on atmosphere soon.

1 Like

Take a look at the Handling NPM callbacks section of the Guide. Since Meteor.bindEnvironment runs asynchronously, it isn’t the best option in your case. Meteor.wrapAsync or Promise.await should give you what you need.

Thanks for reminding me that this is an async Meteor problem. I continued googling wrapAsync and promises and found the meteor chef blog post: https://themeteorchef.com/snippets/synchronous-methods/#tmc-using-futures.

I solved it by following those steps. Thanks for the help, much appreciated! It saved me tons of hours :slight_smile:

2 Likes

How do you prevent FlowRouter from processing that url when you click it?
When I click that link it redirects me to my Frontend FlowRouter Page that has a similar url structure. It only works when I use target="_blank" (would prefer to avoid that though).

Rova,

Did you achieve to implement Mollie succesfully? Would you be so kind to share your method code with me to init the create payment?

Yes @albertsamsir! I did and I processed around 100k (more than 400 payments) successfully with Mollie. So it’s doing the job :slight_smile: !

I have an Autoform where people leave their booking information:

            {{#autoForm
              schema=bookEventSchema
              id="bookEventForm"
              type="method"
              meteormethod="placeBooking"
              doc=emptyEventBooking
              class="booking-form"
            }}

These are my Autoform hooks (don’t mind the Dutch now and then):

AutoForm.hooks
  bookEventForm:
    onSuccess: (operation, result) ->
      # there is only a paymentUrl for paid events. For free events we skip the Mollie payment platform.
      if result.paymentUrl?
        swal
          title: 'Gelukt!'
          type: 'success'
          text: 'Nu bezig met oproepen van het betalingssysteem...'
          showConfirmButton: false
          showCancelButton: false
        window.location.replace result.paymentUrl
      else
        FlowRouter.go 'bookEventConfirmation',
          bookingId: result._id
          citySlug: result.citySlug
    onError: (operation, error) -> sAlert.error 'Het formulier bevat nog fouten, gelieve alles te controleren.'
    beginSubmit: -> $('#form-loading-text').fadeIn()
    endSubmit: -> $('#form-loading-text').fadeOut()

The result.paymentUrl comes from a Meteor Method placeBooking, that is the method attached to the Autoform.
This is the relevant part from the placeBooking method:

    unless @isSimulation
      # check if there is a valid referral discount
      if referrerId?
        try doc = Meteor.call 'applyDiscountCode', bookingId, referrerId
      EventEmitter.emit 'newBooking', bookingId, event, city
      # Create and fetch a payment
      if Settings.get('useOnlinePayments') and not event.isFree() and not event.allowFreeTrial()
        Meteor.call 'createPayment', bookingId, (e, payment) ->
          unless e
            paymentUrl = payment.url
            # use mollie's payment id as the new payment id for the booking
            Bookings.update bookingId, $set: paymentId: payment.id
    return _id: bookingId, citySlug: city.slug, paymentUrl: paymentUrl

I used a seperate method for creating payments: createPayment. The relevant code is here:

Future = Npm.require 'fibers/future'

Meteor.methods
  createPayment: (bookingId) ->
    check bookingId, String
    booking = Bookings.findOne bookingId
    throw new Meteor.Error 'no-booking', 'Geen inschrijving gevonden.' unless booking?
    throw new Meteor.Error 'cannot-be-zero', 'Een gratis betaling?!' if booking.price is 0
    city = booking.event().city()
    future = new Future() # we need to wait for the Async call
    mollie = new Mollie.API.Client
    mollie.setApiKey Meteor.settings.mollie.apiKey
    webhookUrl = if App.inDevelopment() then 'https://cprghddfbi.localtunnel.me/' else Meteor.absoluteUrl 'api/process-payment', secure: Meteor.settings.useSsl
    mollie.payments.create
      amount: booking.price
      description: "#{Settings.get 'organisationName'} Inschrijving"
      redirectUrl: Meteor.absoluteUrl "bevestiging/#{bookingId}/#{city.slug}"
      webhookUrl: webhookUrl
      metadata: bookingId: bookingId
    , (payment) ->
      if payment.error
        console.error payment.error
        throw new Meteor.Error 'payment-error', payment.error.message
      future.return id: payment.id, url: payment.getPaymentUrl()
      return
    return future.wait()

I still use an older version of Meteor, so I cannot use NPM directly, that’s why you see the Future = Npm.require 'fibers/future' on top of the file.

Hope this helps you. Let me know if you have any further questions. Good luck!
PS: what are you building?

1 Like

Thanks Rova! I will try it out with the future package to see what happens, hopefully it will work :slight_smile:
I’m building nothing special atm, just trying to feel how meteor and JS works… coming from PHP

Hey rova,

I am looknig for a payment gateway and looked at Braintree stripe (both dont offer enough payment options for european) and Adyen. I was about to go with adyen until I looked at this thread.

Looks like mollie covers the important payment options and at least from looking at github it seems pretty developer friendly. From a developer point of view was it easy to integrate?

Cheers

Yep, quite easy and their docs are pretty solid. I now have two projects running in production using Mollie to process payments. The perfect provider in my case! Support is fast too, in case you need any help.

1 Like

Ok very cool, just looking at their API guide, its all very clear. Thanks for this, Adyen wouldve been much more complex :slight_smile:

Hi @rova do you know how i can solve this same problem using Iron Router, Meteor 1.5?