Skip to content

Instantly share code, notes, and snippets.

@nilsnolde
Created December 24, 2019 21:28
Show Gist options
  • Save nilsnolde/1283e0b585a9c55763d420e6eb6bd69c to your computer and use it in GitHub Desktop.
Save nilsnolde/1283e0b585a9c55763d420e6eb6bd69c to your computer and use it in GitHub Desktop.
WooCommerce OAuth v1.0 JavaScript client
/* eslint-disable camelcase */
import axios from 'axios'
import OAuth from 'oauth-1.0a'
import crypto from 'crypto-browserify'
import * as R from 'ramda'
import queryString from 'query-string'
/**
* WooCommerce REST API wrappercat
*
* @param {String} consumerKey
* @param {String} consumerSecret
* @param {Number} timeout
*/
class WooCommerceAPI {
constructor(consumerKey, consumerSecret, timeout = 60000) {
this.consumerKey = consumerKey
this.consumerSecret = consumerSecret
this.timeout = timeout
this._request = axios.create({
baseURL: process.env.REACT_APP_WC_API_URL + '/wp-json/wc/v3',
timeout: this.timeout
})
}
/**
* Check for object
*
* @param {object} obj
* @return {bool}
*/
static _isObject(obj) {
return obj === Object(obj)
}
/**
* Normalize query string for oAuth
*
* @param {object} params
* @return {string}
*/
static _normalizeQueryString(params) {
if (!params) {
return ''
}
const params_list = []
for (const p in params) {
params_list.push(p)
}
params_list.sort()
const urlQueryString = {}
for (const idx in params_list) {
/** we can't use POST with oauth which is why we have to make sure
our POST payload (may be nested, objects or arrays) is added correctly
as GET parameters which woocommerce can read **/
if (
Object.prototype.toString.call(params[params_list[idx]]) ===
'[object Object]'
) {
const prepNestedParam = (paramValue, paramKey) => {
urlQueryString[params_list[idx] + '[' + paramKey + ']'] = paramValue
}
R.forEachObjIndexed(prepNestedParam, params[params_list[idx]])
} else if (
Object.prototype.toString.call(params[params_list[idx]]) ===
'[object Array]'
) {
let cnt = 0
for (const item of params[params_list[idx]]) {
if (Object.prototype.toString.call(item) === '[object Object]') {
for (const k in item) {
urlQueryString[
params_list[idx] + '[' + cnt + ']' + '[' + k + ']'
] = item[k]
}
cnt += 1
} else {
urlQueryString[params_list[idx] + '[' + cnt + ']'] = item
}
}
} else {
const paramKey = encodeURIComponent(params_list[idx])
.replace('%5B', '[')
.replace('%5D', ']')
urlQueryString[paramKey] = encodeURIComponent(params[params_list[idx]])
}
}
return '?' + queryString.stringify(urlQueryString, { encode: false })
}
/**
* Get OAuth
*
* @return {OAuth}
*/
_getOAuth() {
const data = {
consumer: {
key: this.consumerKey,
secret: this.consumerSecret
},
signature_method: 'HMAC-SHA1',
hash_function: (base_string, key) => {
return crypto
.createHmac('sha1', key)
.update(base_string)
.digest('base64')
}
}
return new OAuth(data)
}
/**
* GET requests
*
* @param {String} endpoint
* @param {Object} params
*
* @return {Object}
*/
get(endpoint, params = null) {
const method = 'GET'
const urlString = WooCommerceAPI._normalizeQueryString(params)
const oauth_params = this._getOAuth().authorize({
url:
process.env.REACT_APP_WC_API_URL +
'/wp-json/wc/v3' +
endpoint +
urlString,
method: method
})
return this._request.get(endpoint, {
params: { ...params, ...oauth_params }
})
}
post(endpoint, data_params) {
// This is a hack. Problem is WC has some limitations for browser-side OAuth and POST
// Problem: OPTIONS preflights are not supported by WC for POST queries with body and headers
// Solution: put all parameters into querystring, incl OAuth params and don't set headers
// https://github.com/woocommerce/woocommerce/issues/14987#issuecomment-300581467
// https://github.com/woocommerce/woocommerce/issues/15395#issuecomment-305471553
const method = 'POST'
const oauth_params = this._getOAuth().authorize({
url:
process.env.REACT_APP_WC_API_URL +
'/wp-json/wc/v3' +
endpoint +
WooCommerceAPI._normalizeQueryString(data_params),
method: method
})
return this._request.post(endpoint, null, {
params: oauth_params
})
}
put(endpoint, data_params) {
// This is a hack. Problem is WC has some limitations for browser-side OAuth and PUT
// Problem: OPTIONS preflights are not supported by WC for POST queries with body and headers
// Solution: WP introduced the _method parameter for GET to describe what should be done on the backend
// https://github.com/woocommerce/woocommerce/issues/14987#issuecomment-300581467
const _method = 'PUT'
return this.post(endpoint, { ...data_params, _method })
}
}
export default new WooCommerceAPI(
process.env.REACT_APP_WC_CONSUMER_KEY,
process.env.REACT_APP_WC_CONSUMER_SECRET
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment