PayPal을 Vue 애플리케이션과 통합

스타트업의 경우 애플리케이션을 설계하는 동안 고려해야 할 사항이 너무 많습니다. 사용할 프레임워크, 논리를 위한 Typescript 또는 Javascript, 수많은 제품을 사용할 백엔드. 학습 과정에서 저를 진정으로 이끌어준 한 가지 요인은 창업 자금이 문제이고 비용 절감이 염두에 두어야 할 큰 요인이라는 것입니다. 우리는 항상 제품을 리팩토링하거나 변경할 수 있다는 점을 염두에 두고 낮게 시작하여 성장하기를 원합니다.

Slack Payment는 훌륭하지만 아프리카 신생 기업에게는 이것이 도전이 될 것이며 Paypal은 훌륭한 출발점이 될 것입니다. 질문은 왜? PayPal을 사용하면 초기 수수료가 필요하지 않고 비즈니스 계정만 있으면 애플리케이션을 통합할 수 있습니다. 여기에 비즈니스 계정을 등록하려면 링크https://www.paypal.com/ke/webapps/mpp/country-worldwide가 있습니다.
해당 지역 내에서 비즈니스를 등록할 수 있는 국가를 선택하십시오. 비즈니스 계정이 있으면 바로 사용할 수 있습니다.

먼저 Vue 프로젝트를 설정합시다.
  vue create paypal-app
  > Manually select features
  > Babel, Router, Linter, (Vuex, CSS Pre-processors, Unit Testing and E2E testing are always ideal according to your preference)
  > Eslint + Prettier

With that, we will have two things in place that we need to integrate a Vue project and Paypal. Now let's have some components to separate our application. We will have the following components and assemble everything once we are done.

(Views Directory)
Views/
  Home.vue
  Profile.vue

(Components Directory)
Components/
  Payment.vue
  Header.vue
  Upgrade.vue
  Cancel.vue
  Suspend.vue
  Activate.vue

// This will hold our environmental variables. Our secret and client ID
.env 

In our application, we need a user to be able to make the following events:

  1. Select a subscription
  2. Make payment
  3. Cancel or Suspend subscription
  4. Re-Activate a subscription
  5. Upgrade subscription

With the above events, we will have a complete circle for a user in our application. For CSS purposes will be using https://vuetifyjs.com/.

  vue add vuetify
  > Default (recommended)

  // we will be using the following in our application
   yarn add sweetalert2  // alert us when payment is successful
   yarn add axios // to fetch user information

Let's create our application then get our client id and secret id from Paypal thereafter. We'll code each step

홈뷰

This will display all our subscription:

<template>
  <!-- Home Component-->
  <div class="home">
    <Header />
    <v-container>
      <!--Snackbar-->
      <v-snackbar color="info" text v-model="snackbar">
        You have selected <span>{{ selected.title }}</span>
      </v-snackbar>
      <v-row>
        <v-col cols="12" md="4" v-for="(item, index) in items" :key="index">
          <v-radio-group v-model="selected" row @change="selectedsubscription">
            <v-card elevation="5" tile>
              <v-card-text>
                <div class="mb-2 primary--text body-1">{{ item.title }}</div>
                <div class="title black--text">
                  <span>USD</span> {{ item.amount }}
                </div>
                <div>{{ item.text }}</div>
                <v-radio class="mt-4" :value="item"></v-radio>
              </v-card-text>
            </v-card>
          </v-radio-group>
        </v-col>
      </v-row>
    </v-container>
  </div>
</template>

<script>
import Header from "../components/Header";
export default {
  name: "Home",
  components: { Header },
  data() {
    return {
      snackbar: false,
      selected: {},
      items: [
        {
          title: "Basic",
          amount: "5.50",
          text:
            "Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio nam quasi doloremque eos iure iste, quo nostrum ipsum, hic adipisci soluta cum voluptate perspiciatis accusamus quidem non error ratione temporibus.",
          plan_id: "" // plan id
        },
        {
          title: "Essential",
          amount: "6.50",
          text:
            "Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio nam quasi doloremque eos iure iste, quo nostrum ipsum, hic adipisci soluta cum voluptate perspiciatis accusamus quidem non error ratione temporibus.",
          plan_id: "" // plan id
        },
        {
          title: "Premium",
          amount: "7.50",
          text:
            "Lorem ipsum dolor sit amet consectetur adipisicing elit. Odio nam quasi doloremque eos iure iste, quo nostrum ipsum, hic adipisci soluta cum voluptate perspiciatis accusamus quidem non error ratione temporibus.",
          plan_id: "" // plan id
        }
      ]
    };
  },
  methods: {
    // selected subscription function
    selectedsubscription() {
      // check if we are creating a new subscrption or upgrading
      const response = localStorage.getItem("@upgradesubscrption");
      // temporarily save your plan on local storage or use vuex
      // you only need the plan id
      // lets use local storage for beginer purposes
      localStorage.setItem("@selectedpackage", JSON.stringify(this.selected));
      // From here redirect for payment
      // set a timeout so user can see the package they hase selected
      // we can add a snackbar
      this.snackbar = true;
      // You can use one component and write the logic for both upgrade and payment. In my case I have separated them to two components (Payment and Upgrade)
      // Both payment and upgrade requires the user consent
      setTimeout(() => {
        if (response === true) {
          // upgrade
          this.$router.push("/upgrade-payment");
        } else {
          // new subscription
          this.$router.push("/subscription-payment");
        }
      }, 1000);
      // end
    }
  }
};
</script>



플랜 ID를 Paypal 비즈니스 계정의 구독 플랜 ID로 바꾸십시오. 샌드박스에서 테스트하고 라이브로 전환하기 전에 해당 기능을 켜야 합니다.



프로필.vue



이것은 사용자 샘플 프로필 페이지입니다. Paypal에서 가져와 결제 세부 정보를 표시할 수 있습니다.

<template>
  <div class="profile">
    <Header />
    <div class="content">
      <v-navigation-drawer app v-model="drawer">
        <v-card-text class="text-center">
          <v-avatar color="indigo" size="100" class="mb-3"> </v-avatar>
          <Cancel class="mb-2" />
          <Suspend class="mb-2" />
          <Activate class="mb-2" />
          <v-btn
            color="secondary"
            dark
            block
            tile
            @click="upgradesubscription"
            class="pl-5 pr-5"
          >
            <span style="text-transform: capitalize">Upgrade</span>
          </v-btn>
        </v-card-text>
      </v-navigation-drawer>
    </div>
  </div>
</template>

<script>
import Header from "../components/Header";
import Cancel from "../components/Cancel";
import Suspend from "../components/Suspend";
import Activate from "../components/Activate";
export default {
  name: "Profile",
  components: { Header, Suspend, Cancel, Activate },
  data() {
    return {
      drawer: true,
      saving: false
    };
  },
  methods: {
    // upgrade subscrption
    upgradesubscription() {
      // 1. Let's save an upgrade request and subscription id
      let subscription_id = "" // user subscription id
      localStorage.setItem("@upgradesubscrption", true);
      localStorage.setItem("@subscriptionid", JSON.stringify(subscription_id)
      // 2. Send back to select a new package
      this.$router.push("/");
      // end
    }
  }
};
</script>



아래와 같이 프로필 페이지에 사용자 옵션이 표시되었습니다.



헤더.vue




<template>
  <div class="header">
    <v-app-bar app color="grey lighten-5" flat>
      <v-card-title>
        PAYPAL APP
      </v-card-title>
    </v-app-bar>
    <v-divider></v-divider>
  </div>
</template>



활성화.vue



사용자 구독 활성화

<template>
  <div class="activate">
    <v-btn
      :loading="saving"
      color="primary"
      dark
      block
      tile
      @click="activatesubscription"
      class="pl-5 pr-5 mb-2"
    >
      <span style="text-transform: capitalize">Activate</span>
    </v-btn>
  </div>
</template>

<script>
import { activateusersubscription } from "./script";
export default {
  name: "Activate",
  data() {
    return {
      saving: false
    };
  },
  methods: {
    // activate subscription
    async activatesubscription() {
      this.saving = true;
      let subscription_id = ""; //  user subscription id
      activateusersubscription(subscription_id);
      setTimeout(() => {
        this.saving = false;
      }, 1000);
      // end
    }
  }
};
</script>



이 함수를 호출하는 동안 사용자 구독 ID를 제공해야 합니다.

Suspend.vue



사용자 가입 정지

<template>
  <!-- Suspend Component-->
  <div class="suspend-subscription">
    <v-btn
      :loading="saving"
      color="warning"
      dark
      tile
      block
      @click="suspendsubscription"
      class="pl-5 pr-5"
    >
      <span style="text-transform: capitalize">suspend</span>
    </v-btn>
  </div>
</template>

<script>
import { suspendusersubscription } from "./script";
export default {
  name: "Suspend",
  data() {
    return {
      saving: false
    };
  },
  methods: {
    // suspend subscription
    async suspendsubscription() {
      this.saving = true;
      let subscription_id = ""; //  user subscription id
      await suspendusersubscription(subscription_id);
      setTimeout(() => {
        this.saving = false;
      }, 1000);
      // end
    }
  }
};
</script>



이 함수를 호출하는 동안 사용자 구독 ID를 제공해야 합니다.

취소.vue



사용자 구독 취소

<template>
  <!-- Cancel Component-->
  <div class="cancel-subscription">
    <v-btn
      :loading="saving"
      color="red darken-1"
      dark
      tile
      block
      @click="cancelsubscription"
      class="pl-5 pr-5"
    >
      <span style="text-transform: capitalize">Cancel</span>
    </v-btn>
  </div>
</template>

<script>
import { cancelusersubscription } from "./script";
export default {
  name: "Cancel",
  data() {
    return {
      saving: false
    };
  },
  methods: {
    // cancel subscription
    async cancelsubscription() {
      this.saving = true;
      let subscription_id = ""; //  user subscription id
      await cancelusersubscription(subscription_id);
      setTimeout(() => {
        this.saving = false;
      }, 1000);
      // end
    }
  }
};
</script>



이 함수를 호출하는 동안 사용자 구독 ID를 제공해야 합니다.

결제.vue



이 구성 요소를 통해 사용자는 새 구독을 구입할 수 있습니다. 결제/구매 및 업그레이드를 위해 둘 다 사용자가 로그인하고 동의해야 합니다.

<template>
  <!-- Payment Component-->
  <div class="payment">
    <v-sheet height="100vh">
      <v-row justify="center" align="center" class="fill-height">
        <v-col cols="12">
          <div style="display: flex; justify-content: center">
            <div class="sheet-container">
              <v-card-text>
                <div class="paypal-title">{{ packageselect.title }}</div>
                <div class="paypal-text">
                  <span>Total Amount (USD) </span>
                  <span>${{ packageselect.amount }}</span>
                </div>
                <v-divider class="mt-4 mb-4"></v-divider>
                <div id="paypal-button-container"></div>
              </v-card-text>
            </div>
          </div>
        </v-col>
      </v-row>
    </v-sheet>
  </div>
</template>

<script>
import Swal from "sweetalert2";
export default {
  name: "Payment",
  data() {
    return {
      item: {},
      packageselect: ""
    };
  },
  // mounted
  async mounted() {
    // 1. get the selected package
    const package_response = localStorage.getItem("@selectedpackage");
    const package_results = JSON.parse(package_response);
    // 2. Mount paypal button
    if (package_results) {
      this.packageselect = package_results;
      await this.mountpaypalbutton();
    }
    // end
  },
  // methods
  methods: {
    // Mount paypal button
    mountpaypalbutton() {
      // eslint-disable-next-line no-undef
      paypal
        .Buttons({
          style: {
            shape: "rect",
            color: "blue",
            layout: "vertical",
            label: "paypal",
            size: "medium"
          },
          createSubscription: async function(data, actions) {
            // 1. get the selected package
            const package_response = await localStorage.getItem(
              "@selectedpackage"
            );
            const package_results = JSON.parse(package_response);
            let plan_ID = package_results.plan_id; // user plan id
            // 2. Create a subscription
            return actions.subscription.create({
              plan_id: plan_ID
            });
          },
          // eslint-disable-next-line no-unused-vars
          onApprove: async function(data, actions) {
            /**
             * NOTE
             * - Save the subscription id in your Database
             * - This is important to ensure you can always
             * - Check on the status when user logs in or wants
             * - to make payment
             */

            // 2. Save payment subscription id
            //let subscrption_id = data.subscriptionID;

            // 4. Remove the selected package from the local storage

            localStorage.removeItem("@selectedpackage");

            // 5. Lets use swal to give us an alert once transcation is completed
            Swal.fire({
              icon: "success",
              title: "Congratulations",
              text: "Your payment has successfully been proccessed!",
              confirmButtonText: "Complete",
              showLoaderOnConfirm: true,
              preConfirm: () => {
                // redirect user to dashboard or login
                location.assign("http://localhost:8080/profile");
              },
              allowOutsideClick: false
            });
          }
        })
        .render("#paypal-button-container");
    }
  }
};
</script>

<style scooped lang="css">
@media only screen and (max-width: 600px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 300px;
    border-radius: 5px;
  }
}
@media only screen and (min-width: 600px) and (max-width: 960px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 350px;
    border-radius: 5px;
  }
}
@media only screen and (min-width: 960px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 400px;
    border-radius: 5px;
  }
}
.paypal-title {
  -webkit-font-smoothing: antialiased;
  color: black;
  text-transform: uppercase;
  font-size: 1.1em;
  margin-bottom: 5px;
}
.paypal-text {
  -webkit-font-smoothing: antialiased;
  color: black;
  font-size: 1.2em;
  margin-bottom: 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>





업그레이드.vue



업그레이드 또는 다운그레이드 구독은 사용자가 업그레이드 또는 다운그레이드에 동의해야 합니다. 새로운 변경 사항은 다음 결제 시 적용됩니다.

<template>
  <!-- Upgrade Component-->
  <div class="upgrade">
    <v-sheet height="100vh">
      <v-row justify="center" align="center" class="fill-height">
        <v-col cols="12">
          <div style="display: flex; justify-content: center">
            <div class="sheet-container">
              <v-card-text>
                <div class="paypal-title">{{ packageselect.title }}</div>
                <div class="paypal-text">
                  <span>Total Amount (USD) </span>
                  <span>${{ packageselect.amount }}</span>
                </div>
                <v-divider class="mt-4 mb-4"></v-divider>
                <div id="paypal-button-container"></div>
              </v-card-text>
            </div>
          </div>
        </v-col>
      </v-row>
    </v-sheet>
  </div>
</template>

<script>
import Swal from "sweetalert2";
export default {
  name: "Upgrade",
  data() {
    return {
      item: {},
      packageselect: ""
    };
  },
  // mounted
  async mounted() {
    // 1. get the selected package
    const package_response = localStorage.getItem("@selectedpackage");
    const package_results = JSON.parse(package_response);
    // 2. Mount paypal button
    if (package_results) {
      this.packageselect = package_results;
      await this.mountpaypalbutton();
    }
    // end
  },
  // methods
  methods: {
    // Mount paypal button
    mountpaypalbutton() {
      // eslint-disable-next-line no-undef
      paypal
        .Buttons({
          style: {
            shape: "rect",
            color: "gold",
            layout: "vertical",
            label: "paypal",
            size: "medium"
          },
          createSubscription: async function(data, actions) {
            // 1. get the selected package
            const package_response = await localStorage.getItem(
              "@selectedpackage"
            );
            const package_results = JSON.parse(package_response);
  // 2. get subscription id
            const subscription_response = await localStorage.getItem(
              "@subscriptionid"
            );
            let subscription_id = JSON.parse(subscription_response)
            let plan_ID = package_results.plan_id;
            // 2. Revise a subscription
            return actions.subscription.revise(subscription_id,{
              plan_id: plan_ID
            });
          },
          // eslint-disable-next-line no-unused-vars
          onApprove: async function(data, actions) {

            // 4. Remove the selected package from the local storage

            localStorage.removeItem("@selectedpackage");

            // 5. Lets use swal to give us an alert once transcation is completed
            Swal.fire({
              icon: "success",
              title: "Congratulations",
              text: "Your upgrade was succesfull",
              confirmButtonText: "Complete",
              showLoaderOnConfirm: true,
              preConfirm: () => {
                // redirect user to dashboard or login
                location.assign("http://localhost:8080/profile");
              },
              allowOutsideClick: false
            });
          }
        })
        .render("#paypal-button-container");
    }
  }
};
</script>

<style scooped lang="css">
@media only screen and (max-width: 600px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 300px;
    border-radius: 5px;
  }
}
@media only screen and (min-width: 600px) and (max-width: 960px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 350px;
    border-radius: 5px;
  }
}
@media only screen and (min-width: 960px) {
  .sheet-container {
    border: 1px solid #e0e0e0;
    width: 400px;
    border-radius: 5px;
  }
}
.paypal-title {
  -webkit-font-smoothing: antialiased;
  color: black;
  text-transform: uppercase;
  font-size: 1.1em;
  margin-bottom: 5px;
}
.paypal-text {
  -webkit-font-smoothing: antialiased;
  color: black;
  font-size: 1.2em;
  margin-bottom: 15px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>



노트



동일한 제품 내에서 구독을 통해서만 업그레이드할 수 있으므로 계획을 만들 때 모든 계획이 동일한 제품에 있는지 확인하십시오.



Script.js



Paypal에서 사용자 정보를 취소, 일시 중단, 활성화 및 검색하는 기능입니다.

import axios from "axios";
const client_id = process.env.VUE_APP_PAYPAL_CLIENT_ID;
const client_secret = process.env.VUE_APP_PAYPAL_CLIENT_SECRET_ID;

/**
 * If you find challege then concat the url with + instead of html integral
 * "https://api-m.sandbox.paypal.com/v1/billing/subscriptions/" + subscriptionID + "/suspend"
 * =========================================================================================
 */

// retirive user subscription details
export async function retriveuserinformation(subscriptionID) {
  // 1. Call PayPal to get a token
  const auth = await axios({
    url: "https://api-m.sandbox.paypal.com/v1/oauth2/token",
    method: "post",
    headers: {
      Accept: "application/json",
      "Accept-Language": "en_US",
      "content-type": "application/x-www-form-urlencoded"
    },
    auth: {
      username: client_id,
      password: client_secret
    },
    params: {
      grant_type: "client_credentials"
    }
  });
  // 2. Call PayPal to get the subscription details
  const user_subscription_details = await axios({
    url: `https://api-m.sandbox.paypal.com/v1/billing/subscriptions/${subscriptionID}`,
    method: "get",
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${auth.data.access_token}`
    }
  });
  return user_subscription_details;
  // end
}

// suspend user subscription
export async function suspendusersubscription(subscriptionID) {
  // 1. Call PayPal to get a token
  const auth = await axios({
    url: "https://api-m.sandbox.paypal.com/v1/oauth2/token",
    method: "post",
    headers: {
      Accept: "application/json",
      "Accept-Language": "en_US",
      "content-type": "application/x-www-form-urlencoded"
    },
    auth: {
      username: client_id,
      password: client_secret
    },
    params: {
      grant_type: "client_credentials"
    }
  });
  // 2. Call PayPal to get the subscription details
  const user_subscription_details = await axios({
    url: `https://api-m.sandbox.paypal.com/v1/billing/subscriptions/${subscriptionID}/suspend`,
    method: "post",
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${auth.data.access_token}`
    },
    data: {
      reason: "Customer-requested pause" // reason
    }
  });
  return user_subscription_details;
  // end
}

// suspend user subscription
export async function cancelusersubscription(subscriptionID) {
  // 1. Call PayPal to get a token
  const auth = await axios({
    url: "https://api-m.sandbox.paypal.com/v1/oauth2/token",
    method: "post",
    headers: {
      Accept: "application/json",
      "Accept-Language": "en_US",
      "content-type": "application/x-www-form-urlencoded"
    },
    auth: {
      username: client_id,
      password: client_secret
    },
    params: {
      grant_type: "client_credentials"
    }
  });
  // 2. Call PayPal to get the subscription details
  const user_subscription_details = await axios({
    url: `https://api-m.sandbox.paypal.com/v1/billing/subscriptions/${subscriptionID}/cancel`,
    method: "post",
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${auth.data.access_token}`
    },
    data: {
      reason: "Customer-requested pause" // reason
    }
  });
  return user_subscription_details;
  // end
}

// activate user subscription
export async function activateusersubscription(subscriptionID) {
  // 1. Call PayPal to get a token
  const auth = await axios({
    url: "https://api-m.sandbox.paypal.com/v1/oauth2/token",
    method: "post",
    headers: {
      Accept: "application/json",
      "Accept-Language": "en_US",
      "content-type": "application/x-www-form-urlencoded"
    },
    auth: {
      username: client_id,
      password: client_secret
    },
    params: {
      grant_type: "client_credentials"
    }
  });
  // 2. Call PayPal to get the subscription details
  const user_subscription_details = await axios({
    url: `https://api-m.sandbox.paypal.com/v1/billing/subscriptions/${subscriptionID}/activate`,
    method: "post",
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${auth.data.access_token}`
    },
    data: {
      reason: "Reactivating on customer request"
    }
  });
  return user_subscription_details;
  // end
}



이를 통해 모든 섹션을 준비하고 실행할 수 있습니다. 사용하려는 경우 Github 저장소가 있습니다https://github.com/kevinodongo/paypal-app.git.

이제 Paypal로 돌아가서 통합을 완료하고 애플리케이션을 테스트하는 데 필요한 것을 가져오겠습니다. 여기에서 로그인할 수 있는 Paypal의 개발자 섹션에서 앱을 생성해야 합니다https://developer.paypal.com/developer/applications/.

앱을 만든 후 앱을 클릭하면 자격 증명이 검색됩니다. 다음이 필요합니다.
  • 클라이언트 ID
  • 비밀

  • 그런 다음에는 public 폴더에 있는 index.html에 있는 애플리케이션으로 돌아가야 합니다.

    // Replace the client id with your app client id.
      <script
        src="https://www.paypal.com/sdk/js?client-id=${CLIENT ID}"
        data-sdk-integration-source="button-factory"
      ></script>
    


    이제 애플리케이션을 실행할 수 있습니다.
      yarn serve || yarn run serve
    

    다음 단계



    통합을 테스트하여 올바른 통신이 이루어지고 있는지 확인합니다. 샌드박스 URL을 실제 URL로 교체하기 전에 샌드박스에서 테스트합니다.

    Paypal 개발자 대시보드에서 내 계정으로 이동하여 비즈니스 및 개인 계정을 만듭니다.

    비즈니스를 사용하여 샌드박스 계획을 만들고 테스트 중에 해당 구독 계획 ID를 사용합니다.

    거기에 PayPal과 Vue의 통합이 있습니다. 위의 다음 기사는 Lambda AWS 및 Paypal webhook를 사용하는 것입니다.

    좋은 웹페이지 즐겨찾기