Wix Store Webhook for Order Paid, Cancelled and Refunded

Hello,

I am currently working on a project that involves processing order data for books whenever a customer pays for their order. I want to assign a book to the user on a third-party platform using webhooks. However, after researching Wix Webhooks, I found that the automation menu only provides triggers for orders placed and cart abandonment, and these triggers do not send all the necessary data I require. Additionally, there is no hash/hmac payload verification or webhook retry mechanism.

I came across a solution on https://dev.wix.com/api/rest/wix-stores/orders/order-paid-webhook that meets my requirements, but it is only available to Wix apps. Creating a Wix app requires hosting a front-end and server and implementing OAuth, which is not what I am looking for.

My question is, if this webhook is available to Wix apps, why is it not available to Wix sites? Since the webhook data is already implemented in Wix Core, I believe Wix should be able to send webhook data to any URL specified by the user. Is there a way to achieve this without creating a Wix app?

My last resort is to send a post request from the onOrderPaid event, but this would require giving up on the webhook retry feature as the event is only fired once.

I would appreciate any help you could provide. Thank you.

Hey there,
I have exactly the same issue. Did you come up with a solution yet, or did you use the onOrderPaid?

No, I used onOrderPaid with axios and jwt.

Thanks for the fast reply, would you mind sharing your code with us? So we get an idea of how feasable it is to do. Im still getting my head around why Wix is forcing us to create an app for this functionality…

I don’t have the retry mechanism code but I have a working snippet here (initial version):

back-end/events.js

import * as axios from 'axios';
import * as jwt from 'jsonwebtoken';
import {getSecret} from 'wix-secrets-backend';

//...
export async function wixStores_onOrderPaid(event) {
  try {
    const paidOrderId = event._id;
    const rsa_private_key = await getSecret("PRIVATE_KEY");
    const rsa_alg = await getSecret("KEY_ALGORITHM");
    const body = jwt.sign(event, rsa_private_key, { algorithm: rsa_alg });
    console.log(body);
    const order_paid = await getSecret("ORDER_PAID");
    const order_paid_topicname = await getSecret("ORDER_PAID_TOPIC");
    const response = await axios.post(order_paid, body, { headers: { 'x-wix-topic': order_paid_topicname } })
    console.log(response);
  } catch (error) {
    console.log('error in processing order paid webhook trigger', error);
  }
}

Remember to install below dependencies:

axios (v.1.3.5)
jsonwebtoken (v.8.5.1)

Learn about Wix Secret Manager: Velo: About the Secrets Manager | Help Center | Wix.com

You can add your own retry mechanism to it.

1 Like

Thank you so much! Much appreciated!

Also working through event listeners for the first time and sending wix event data back to our server. Here is my first pass, with guidance from @ashish-yadav’s post above. Simple immediate retry implemented but could likely be enhanced a bit.

(hope this helps someone in the same boat…)

/*****************
 backend/events.js
 *****************/

import * as axios from 'axios';
import * as jwt from 'jsonwebtoken';
import {getSecret} from 'wix-secrets-backend';

// Generate JWT for payload and send post request to server
async function sendPostRequest (payload, subject) {

  // Gather secrets
  const privateKey = await getSecret('privateKey');
  const url = await getSecret('endpointUrl');
  const jti = await getSecret('jti');
  
  // Construct JWT claims
  const claims = {
    "issuer": "wix.com",
    "subject": subject,
    "audience": [ url ],
    "expiresIn": "1h", // 1 hour
    "notBefore": "-2m", // 2 minutes
    "jwtid": jti
  };

  // Generate JWT by signing payload
  let token = null;
  try {
    token = jwt.sign(payload, privateKey, claims);
  } catch (error) {
    console.log('ERROR: unable to generate JWT for ' + subject, error);
  }
  
  // Send JWT in HTTP POST request
  let response = null;
  if (token != null) {
    try {
      response = await axios.post(url, null, { params: { jwt: token } });
      console.log(response);
    } catch (error) {
      console.log('ERROR: invalid mychapter post request', error);
    }
  }

  // Return response from server
  return response;
}

// WixStore NewOrder event listener
export async function wixStores_onNewOrder(event) {

  // Send request to server
  let response = await sendPostRequest (event, "WixStore NewOrder");

  // Retry once if needed
  if (response == null) {
    response = await sendPostRequest (event, "WixStore NewOrder");
    if (response == null) {
      console.log('ERROR: Unable to POST WixStore NewOrder - retry failed');
    }
  }
  
  // Return response from server
  return response;
}

// WixPricingPlans PlanPurchased event listener
export async function wixPricingPlans_onPlanPurchased(event) {

  // Send request to server
  let response = await sendPostRequest (event, "WixPricingPlans PlanPurchased");

  // Retry once if needed
  if (response == null) {
    response = await sendPostRequest (event, "WixPricingPlans PlanPurchased");
    if (response == null) {
      console.log('ERROR: Unable to POST WixPricingPlans PlanPurchased - retry failed');
    }
  }
  
  // Return response from server
  return response;
}
1 Like