Jump to content

Seb

Administrators
  • Posts

    674
  • Joined

  • Last visited

Posts posted by Seb

  1. We've just deployed 'deferred provision fields' which allows you to tell clients to configure products after the order is placed. This is quite a demanded feature but it's a substantial change.

    You can enable it either on a per product basis, or on a product category basis

    You can choose to collect all data (e.g. domain registrant info) up front or after an order. You can also allow the customer to do both.

    Please test and let us know if any problems!

    • Like 4
  2. That's a WordPress SSO and I can't see how it could be the same provision config. It probably needs a standalone 'wordpress on enhance' provision module

    For example

     - How do we know a site runs wordpress and we should show the wordpress login

     - How do we know which site it's for given that an enhance package can have many sites.

    • Like 1
  3. Two very separate things here

    On restricting payment methods to countries, I've added an issue for that.

    On converting a product price, can you explain what you mean. A country doesn't have a product price - that's defined by currency. You can already restrict gateways by currency.

    • Like 1
  4. On 11/6/2022 at 10:46 AM, MIKE said:

    Yep i already know that. what I am saying is that when I am a paying customer. this should be included in the price which covers all the costs. I think many customers here agree.

     

    Honestly, most of our paying customers don't care about the branding and keep it. If this wasn't an optional extra we'd have to increase prices for everyone.

    • Like 3
  5. 1. What would happen in the case of a product free for the first 3 months then the price changes to $5 per month? 

    We store on the contract product at the time of ordering so it would revert to the price originally stored not the new catalogue price.

    2. What happens to existing customers where product prices need to be increased each year? 

    You would have to change them on the individual existing contract products. We can create tooling and endpoints to do that in bulk.

    3. Can clients cancel a free product? 

    Yes

  6. I don't think they have checkout flows yet. The only thing we could do is basically get a webhook when a payment arrives -- but then we'd have to try and match it with an account and then associate with invoices / add to credit if it doesn't match, as well as flag payments that we can't match - I think it gets messy and we'd be better to wait for them to have a checkout flow.

    Other gateways do this great -- like Paystack and Flutterwave. They basically provide let you initiate a payment, then you pay via bank transfer, and then they say the payment is complete.

    • Like 1
  7. I just put a very simple proof of concept together for a client to give them endpoints, so am putting this here in case any one wants to use it as a base for something. This is not production-ready code and I've just put it together very quickly as an example. But it would let a client enter their username and password to Upmind and then log them directly in to their service (e.g. cPanel) as long as they only had one service:

    <?php
    // Input fields
    $brandurl = "https://my.brandurl.com";
    
    class upmindAuth {
    
        public $api_url;
        public $origin;
        public $token;
    
        function __construct($origin){
            $this->api_url = 'api.upmind.io'; //live api url
            $this->origin = $origin; // Brand Origin
        }
    
        public function validate($username,$password){
            if (!$this->api_token('/oauth/access_token',array('username'=>$username,'password'=>$password,'grant_type'=>'password'))){
              return false;
            }
            $account = $this->get('/api/accounts');
            $this->post('/api/accounts/select',array('account_id'=>$account->data[0]->id));
            return $this->get('/api/clients/'.$account->data[0]->pivot->client_id.'?with=custom_fields,tags');
        }
    
        public function api_token($endpoint,$params){
    
            $curl = curl_init();
            curl_setopt_array($curl, array(
              CURLOPT_URL => "https://".$this->api_url."/oauth/access_token",
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_ENCODING => "",
              CURLOPT_MAXREDIRS => 10,
              CURLOPT_TIMEOUT => 30,
              CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
              CURLOPT_CUSTOMREQUEST => "POST",
              CURLOPT_POSTFIELDS => json_encode($params),
              CURLOPT_HTTPHEADER => array(
                "accept: application/vnd.pub.v1+json",
                "cache-control: no-cache",
                "content-type: application/json",
                "origin: $this->origin",
              ),
            ));
    
            $response = curl_exec($curl);
            $err = curl_error($curl);
    
            if ($err) {
                return array('success'=>false,'error'=>"cURL Error #:" . $err);
            } else {
                $actor = json_decode($response);           
                if ($actor->status == 'error') {
                    return false;
                } 
                if ($actor->second_factor_required){
                  $this->token = $actor->access_token;
                  $authresponse = $this->post('/oauth/access_token',array('grant_type'=>'twofa','twofa_code'=>$_POST['2fakey']));
                  if ($authresponse->status == 'error'){  
                    return false;
                  } 
                  $this->token = $authresponse->access_token;
                  return true;
                }
                $this->token = $actor->access_token;
                return true;
            }
        }
    
        public function get($endpoint){
          
            $curl = curl_init();
            curl_setopt_array($curl, array(
            CURLOPT_URL => "https://".$this->api_url.$endpoint,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "GET",
            CURLOPT_HTTPHEADER => array(
              "authorization: Bearer ".$this->token,
              "cache-control: no-cache",
              "origin: ".$this->origin,
            ),
          ));
    
          $response = curl_exec($curl);
          $err = curl_error($curl);
          
          if ($err) {
            echo "cURL Error #:" . $err;
          } else {
              return json_decode($response);
          }
        }
    
        public function post($endpoint,$params){
    
          $curl = curl_init();
          curl_setopt_array($curl, array(
          CURLOPT_URL => "https://".$this->api_url.$endpoint,
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_ENCODING => "",
          CURLOPT_MAXREDIRS => 10,
          CURLOPT_TIMEOUT => 30,
          CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
          CURLOPT_CUSTOMREQUEST => "POST",
          CURLOPT_POSTFIELDS => http_build_query($params),
          CURLOPT_HTTPHEADER => array(
            "authorization: Bearer ".$this->token,
            "cache-control: no-cache",
            "origin: ".$this->origin,
          ),
        ));
    
        $response = curl_exec($curl);
        $err = curl_error($curl);
        
        if ($err) {
          echo "cURL Error #:" . $err;
        } else {
            return json_decode($response);
        }
      }
    
    }
    
    $upmind = new upmindAuth($brandurl);
    $userobject = $upmind->validate($_POST['email'],$_POST['password']);
    if (!$userobject){
        $values['error'] = "Login details incorrect";
    } else {
        $contractproducts = $upmind->get('/api/contracts_products?filter[status.code]=contract_active');
        if (sizeof($contractproducts->data) !== 1){
            die('not just one product');
        }
        $provisionfunctions = $upmind->get('/api/contracts/' . $contractproducts->data[0]->contract_id . '/products/' . $contractproducts->data[0]->id.'/provision_functions');
        foreach ($provisionfunctions->data as $f){
            if ($f->name == 'getLoginUrl'){
                $login = $upmind->post(
                    '/api/contracts/' . $contractproducts->data[0]->contract_id . '/products/' . $contractproducts->data[0]->id.'/provision_requests',
                    array(
                        'provision_field_values' => array(),
                        'blueprint_function_id' => $f->id
                ));
                foreach ($login->data->action_logs as $l){
                    if (isset($l->data->redirect_url)){
                        header("Location: " . $l->data->redirect_url);
                    }
                } 
            }
        }
    }
    
                   
    ?>
    
    <form method="post">
        <input type="text" name="email" placeholder="[email protected]"><br />
        <input type="password" name="password" placeholder="password"><br />
        <input type="text" name="2fakey" placeholder="2fa (optional)"><br />
    <input type="submit">

     

  8. We've created an issue for BCC and CCing emails. I think it's best if it's an option on the email template, because there are some emails you might not want to be CC'd on, and others we would really want to block (password resets etc).

    On MailChimp, we'll work on this in the same way we're doing WebHooks, CampaignMonitor and SMS messages, as a notification channel. I can't give an ETA but reworking the notification channels is quite important.

    • Like 2
  9. An invoice can't be cancelled. That doesn't exist in accounting. It can only be credited. An invoice is an official tax document and if you want to write it off you need a credit note on the other side of the ledger to balance it against. WHMCS is in many areas an example of how invoicing shouldn't be done.

    Legacy invoices are ones that have come from a previous system. They aren't ones you can pay. We generate new invoices based on the service due dates, billing cycle and recurring amounts. Legacy invoices we couldn't map to periods for reporting/MRR/churn rate calculations etc.

     

    • Like 1
  10. Yes, we don't set ETAs because we need to be flexible and change our development cycle and roadmap to adapt to issues we find or important features we need to add. When we are out of beta we will publish a roadmap.

    • Like 2
  11. 14 hours ago, Feedback Monkey said:

    They've also got SSO directly into WP now also via API, can we get some functionality to choose on the Enhance provision module, SSO into Enhance or Directly to WP.

     

    I think most here would like their customers to directly SSO into WP.

    We could probably just show both

  12. A few reasons - it added complexity to signup, we didn't want to get to the stage where people were using other brand names, people were already starting to ask us to change it 🙂

    We'll likely bring some options in in future where you can set a custom one with a variety of top levels. (e.g. yourbrand.[generic.com])

    With SES we have a limit of 5,000 top level senders (e.g. generic.com) which is why we can't send out from actual brand domains.

    • Like 2
    • Sad 1
×
×
  • Create New...