chargebee / chargebee-typescript

Typescript library for the Chargebee API.
https://apidocs.chargebee.com/docs/api?lang=typescript
MIT License
23 stars 16 forks source link

Impossible to have multiple chargebee instances with different ENV variables #42

Closed armen19932 closed 6 months ago

armen19932 commented 1 year ago

Hi, I need to have multiple instances in my application, but I can't implement it, because when I'm setting up the instances and configuring it, as ENV it is getting the last ENV variable I've set. I've investigated the problem and I find, that the _env in Chargebee class is static, and always it is getting the last configuration

SevanEl commented 1 year ago

@armen19932 I checked the code and in fact there cannot be 2 instances of the ChargeBee class with different environments. Since the _env is a static member of the ChargeBee class, the call to ChargeBee.configure() method is going to override the existing values in the static _env member of the ChargeBee, thus effecting the environment for all instances of ChargeBee class.

On the other hand, though the above mentioned issue is real, and does not allow you to have two different instances of the ChargeBee class to be used for EU and US payments, you can have just one instance, and use the RequestWrapper.request() method to pass the expected environment variables upon each call.

    public request(callBack=undefined, envOptions?): Promise<T> {
        let env = {};
        let jsonConstructor =  {}.constructor;
        Util.extend(true, env, this.envArg);
        if (typeof envOptions !== 'undefined') {
            Util.extend(true, env, envOptions);
        } else if(typeof callBack !== 'undefined' && callBack.constructor === jsonConstructor && !Util.isFunction(callBack)){
            Util.extend(true, env, callBack);
            callBack = undefined;
        }
        let deferred = Util.createDeferred<T>(callBack);
        let urlIdParam = this.apiCall.hasIdInUrl ? this.args[0] : null;
        let params = this.apiCall.hasIdInUrl ? this.args[1] : this.args[0];
        if (typeof callBack !== 'undefined' && !Util.isFunction(callBack)) {
            throw new Error('The callback parameter passed is incorrect.');
        }
        function callBackWrapper(err, response) {
            if (err) {
                deferred.reject(err);
            } else {
                deferred.resolve(response);
            }
        }
        Core.makeApiRequest(env, callBackWrapper, this.apiCall.httpMethod, this.apiCall.urlPrefix, this.apiCall.urlSuffix, urlIdParam, params, this.httpHeaders, this.apiCall.isListReq);
        return deferred.promise;
    };

As you can see, the request() method has an envOptions parameter which can be used to pass the expected environmental options upon each call. The implementation checks for typeof envOptions !== 'undefined' and overrides the values with the ones from the this.envArg.

So, having only one ChargeBee class instance, you can do as follows:

import { ChargeBee } from 'chargebee-typescript';
const chargebee = new ChargeBee();

const { customerEU } = await chargebee.customer
  .create({
    email: 'john@test.eu',
    // other params
  })
  .request(callBack=undefined, { site: 'YOUR_EU_SITE_NAME', api_key: 'YOUR_EU_API_KEY' });

const { customerUS } = await chargebee.customer
  .create({
    email: 'john@test.us',
    // other params
  })
  .request(callBack=undefined, { site: 'YOUR_US_SITE_NAME', api_key: 'YOUR_US_API_KEY' });

Note: This does not solve the issue of nothing being able to have two separate instances of ChargeBee class, each with its own environment values, but can work as a workaround.

Liam-Tait commented 8 months ago

Thank you @SevanEl

cb-sriramthiagarajan commented 6 months ago

Hello @armen19932, I'm sorry for the delay in responding to this. Did the solution provided by @SevanEl (thank you Sevan 😄) work for you?

I'm also curious what's the use case you're trying to solve? Is it also for calling US & EU sites for different users, like the one given by Sevan?

cb-sriramthiagarajan commented 6 months ago

@armen19932 — can you please confirm this works for you? This is also documented here in the "Making API calls to specific Chargebee sites" section.

armen19932 commented 6 months ago

@armen19932 — can you please confirm this works for you? This is also documented here in the "Making API calls to specific Chargebee sites" section.

Hi @SevanEl 's answer is working for me but the one is described in the official documentation is not working, because the env variable is stored in the static variable (it was this way, when I was writing code, maybe not it is fixed), and when I wanted to configure one instance, the same configuration was applied to another instances also

cb-sriramthiagarajan commented 6 months ago

Thank you @armen19932. I can see that the way given in our Docs works as well. Sharing a sample code snippet below.

chargebee.configure({
  api_key: "site1-api-key",
  site: "site1"
});

try {
    const result = await chargebee.customer
      .list({
        limit: 1
      })
      .request(undefined, {
        site: "site2",
        api_key: "site2-api-key"
      });
    console.log("site2 customer", result.list[0]?.customer.id);
  } catch (error) {
    console.log(error);
  }

  try {
    const result = await chargebee.customer
      .list({
        limit: 1
      })
      .request();
    console.log("site1 customer", result.list[0]?.customer.id);
  } catch (error) {
    console.log(error);
  }

I'll go ahead and close this issue for now. Please feel free to reopen or create a new one if you face this again. Thanks.