first commit

This commit is contained in:
Your Name
2026-01-19 14:19:22 +08:00
commit fe2d9c1868
4777 changed files with 665503 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

View File

@@ -0,0 +1,6 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 75
}

View File

@@ -0,0 +1,134 @@
import path from 'path'
import versions from './versions'
const latest = versions[0]
const nav = [
{
text: '首页',
link: '/'
},
{
text: '文档',
activeMatch: `^/([0-9]\.x)/`,
items: versions.map((version) => ({
text: version,
link: `/${version}/`
}))
},
{
text: '视频',
link: 'https://wiki.w7.cc/college/collectiondetail/3'
},
{
text: '讨论',
link: 'https://github.com/w7corp/easywechat/discussions'
},
{
text: '赞助',
link: 'https://github.com/sponsors/overtrue'
}
]
export const sidebar = versions.reduce(
(sidebars, version) => ({
...sidebars,
[`/${version}/`]: require(path.join(
__dirname,
`../src/${version}/sidebar`
))
}),
{}
)
export default {
lang: 'zh-CN',
title: 'EasyWeChat',
description: '一个 PHP 微信开发 SDK',
srcDir: 'src',
srcExclude: [],
scrollOffset: 'header',
head: [
['link', { rel: 'icon', href: '/favicon.svg' }],
['meta', { name: 'twitter:site', content: '@easywechat' }],
['meta', { name: 'twitter:card', content: 'summary' }],
[
'meta',
{
name: 'twitter:image',
content: 'https://easywechat/logo.png'
}
]
],
themeConfig: {
nav,
sidebar,
logo: '/logo-icon.svg',
algolia: {
indexName: 'easywechat',
appId: 'X3KJL5SQXD',
apiKey: '5c5ba71b35c48411f245bef4c695fc36'
// searchParameters: {
// facetFilters: ['version:v3']
// }
},
// carbonAds: {
// code: '',
// placement: ''
// },
socialLinks: [
{ icon: 'github', link: 'https://github.com/w7corp/easywechat' },
{ icon: 'twitter', link: 'https://twitter.com/overtrue' }
],
editLink: {
pattern:
'https://github.com/w7corp/EasyWeChat/edit/6.x/docs/src/:path',
text: '帮助我们改善此页面!'
},
license: {
text: 'MIT License',
link: 'https://opensource.org/licenses/MIT'
},
copyright: `Copyright © 2013-${new Date().getFullYear()} 微擎 <a class="ml-4" href="https://beian.miit.gov.cn/" target="_blank">皖ICP备19002904号-6</a>`
},
vite: {
define: {
__VUE_OPTIONS_API__: false
},
optimizeDeps: {
include: ['gsap', 'dynamics.js'],
exclude: []
},
// @ts-ignore
ssr: {
external: []
},
server: {
host: true,
fs: {
// for when developing with locally linked theme
allow: ['../..']
}
},
build: {
minify: 'terser',
chunkSizeWarningLimit: Infinity
},
json: {
stringify: true
}
},
vue: {
reactivityTransform: true
}
}

View File

@@ -0,0 +1,55 @@
<script setup>
/**
* Adding a new banner:
* 1. uncomment the banner slot in ../index.ts
* 2. uncomment and update BANNER_ID in ../../inlined-scripts/restorePreferences.ts
* 3. update --vt-banner-height if necessary
*/
let open = $ref(true)
/**
* Call this if the banner is dismissible
*/
function dismiss() {
open = false
document.documentElement.classList.add('banner-dismissed')
localStorage.setItem(`vue-docs-banner-${__VUE_BANNER_ID__}`, 'true')
}
</script>
<template>
<div class="banner" v-if="open"></div>
</template>
<style>
html:not(.banner-dismissed) {
--vt-banner-height: 24px;
}
</style>
<style scoped>
.banner {
position: fixed;
z-index: var(--vp-z-index-banner);
box-sizing: border-box;
top: 0;
left: 0;
right: 0;
height: var(--vt-banner-height);
line-height: var(--vt-banner-height);
text-align: center;
font-size: 12px;
font-weight: 600;
color: #fff;
background-color: var(--vt-c-green);
}
.banner-dismissed .banner {
display: none;
}
a {
text-decoration: underline;
}
</style>

View File

@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { useData } from 'vitepress'
const { theme } = useData()
</script>
<template>
<div
class="text-center border-t dark:border-black leading-loose py-6 text-xs"
>
<p v-if="theme.license" class="license">
Released under the
<a class="link" :href="theme.license.link" no-icon>
{{ theme.license.text }} </a
>.
</p>
<p
v-if="theme.copyright"
class="copyright"
v-html="theme.copyright"
></p>
</div>
</template>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import SponsorsGroup from './SponsorsGroup.vue'
import { useData } from 'vitepress'
const { frontmatter } = useData()
</script>
<template>
<div v-if="frontmatter.sponsors !== false">
<a class="sponsors-aside-text">Sponsors</a>
<SponsorsGroup tier="special" />
<SponsorsGroup tier="platinum" />
</div>
</template>
<style>
a.sponsors-aside-text {
color: var(--vt-c-text-3);
display: block;
margin: 3em 0 1em;
font-weight: 700;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.4px;
}
</style>

View File

@@ -0,0 +1,201 @@
<script lang="ts">
interface Sponsor {
url: string
img: string
name: string
}
interface SponsorData {
special: Sponsor[]
platinum: Sponsor[]
platinum_china: Sponsor[]
gold: Sponsor[]
silver: Sponsor[]
bronze: Sponsor[]
}
// shared data across instances so we load only once
let data = $ref<SponsorData>()
let pending = false
</script>
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
const { tier, placement = 'aside' } = defineProps<{
tier: keyof SponsorData
placement?: 'aside' | 'page' | 'landing'
}>()
let container = $ref<HTMLElement>()
let visible = $ref(false)
onMounted(async () => {
// only render when entering view
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
visible = true
observer.disconnect()
}
},
{ rootMargin: '0px 0px 300px 0px' }
)
observer.observe(container)
onUnmounted(() => observer.disconnect())
// load data
if (!pending) {
pending = true
// data = await (await fetch(`${base}/data.json`)).json()
}
})
</script>
<template>
<div
ref="container"
class="sponsor-container"
:class="[tier.startsWith('plat') ? 'platinum' : tier, placement]"
>
<template v-if="data && visible">
<a
v-for="{ url, img, name } of data[tier]"
class="sponsor-item"
:href="url"
target="_blank"
rel="sponsored noopener"
>
<picture v-if="img.endsWith('png')">
<source
type="image/avif"
:srcset="`${base}/images/${img.replace(/\.png$/, '.avif')}`"
/>
<img :src="`${base}/images/${img}`" :alt="name" />
</picture>
<img v-else :src="`${base}/images/${img}`" :alt="name" />
</a>
</template>
<a
v-if="placement !== 'page' && tier !== 'special'"
href="https://github.com/sponsors/overtrue"
class="sponsor-item action"
>Your logo</a
>
</div>
</template>
<style scoped>
.sponsor-container {
--max-width: 100%;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(var(--max-width), 1fr));
column-gap: 4px;
}
.sponsor-container.platinum {
--max-width: 240px;
}
.sponsor-container.gold {
--max-width: 180px;
}
.sponsor-container.silver {
--max-width: 140px;
}
.sponsor-item {
margin: 2px 0;
background-color: var(--vt-c-white-soft);
display: flex;
justify-content: space-around;
align-items: center;
border-radius: 2px;
transition: background-color 0.2s ease;
height: calc(var(--max-width) / 2 - 6px);
}
.sponsor-item.action {
font-size: 11px;
color: var(--vt-c-text-3);
}
.sponsor-item img {
max-width: calc(var(--max-width) - 30px);
max-height: calc(var(--max-width) / 2 - 20px);
}
.special .sponsor-item {
height: 160px;
}
.special .sponsor-item img {
max-width: 300px;
max-height: 150px;
}
/* dark mode */
.dark .aside .sponsor-item,
.dark .landing .sponsor-item {
background-color: var(--vt-c-bg-soft);
}
.aside .sponsor-item img,
.landing .sponsor-item img {
transition: filter 0.2s ease;
}
.dark .aside .sponsor-item img,
.dark .landing .sponsor-item img {
filter: grayscale(1) invert(1);
}
.dark .aside .sponsor-item:hover,
.dark .landing .sponsor-item:hover {
color: var(--vt-c-indigo);
background-color: var(--vt-c-white-mute);
}
.dark .sponsor-item:hover img {
filter: none;
}
/* aside mode (on content pages) */
.sponsor-container.platinum.aside {
--max-width: 110px;
column-gap: 1px;
}
.aside .sponsor-item {
margin: 1px 0;
}
.aside .special .sponsor-item {
width: 100%;
height: 60px;
}
.aside .special .sponsor-item img {
width: 120px;
}
.aside .platinum .sponsor-item {
width: 111px;
height: 50px;
}
.aside .platinum .sponsor-item img {
max-width: 88px;
}
/* narrow, aside will be hidden under this state so it's mutually exclusive */
@media (max-width: 720px) {
.sponsor-container.platinum {
--max-width: 180px;
}
.sponsor-container.gold {
--max-width: 140px;
}
.sponsor-container.silver {
--max-width: 120px;
}
}
@media (max-width: 480px) {
.sponsor-container.platinum {
--max-width: 150px;
}
.sponsor-container.gold {
--max-width: 120px;
}
.sponsor-container.silver {
--max-width: 100px;
}
}
</style>

View File

@@ -0,0 +1,7 @@
<template>
<sup
class="bg-green-500 text-xs text-white px-2 py-1 rounded-lg align-top rounded-bl-none"
title="该特性需要更新到此版本可用"
><slot
/></sup>
</template>

View File

@@ -0,0 +1,21 @@
import './styles/index.css'
import { h, App } from 'vue'
import SponsorsAside from './components/SponsorsAside.vue'
import VersionTag from './components/VersionTag.vue'
import Footer from './components/Footer.vue'
import DefaultTheme from 'vitepress/theme'
export default Object.assign({
...DefaultTheme,
Layout: () => {
// @ts-ignore
return h(DefaultTheme.Layout, null, {
// banner: () => h(Banner),
'aside-mid': () => h(SponsorsAside),
'layout-bottom': () => h(Footer)
})
},
enhanceApp({ app }: { app: App }) {
app.component('version-tag', VersionTag)
}
})

View File

@@ -0,0 +1,28 @@
.vt-badge.wip:before {
content: 'WIP';
}
.vt-badge.ts {
background-color: #3178c6;
}
.vt-badge.ts:before {
content: 'TS';
}
.vt-badge.dev-only,
.vt-badge.experimental {
color: var(--vt-c-text-light-1);
background-color: var(--vt-c-yellow);
}
.vt-badge.dev-only:before {
content: 'Dev only';
}
.vt-badge.experimental:before {
content: 'Experimental';
}
.vt-badge[data-text]:before {
content: attr(data-text);
}

View File

@@ -0,0 +1,11 @@
@import './layout.css';
@import './pages.css';
@import './badges.css';
@import './options-boxes.css';
@import './inline-demo.css';
@import './utilities.css';
@import './style-guide.css';
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,90 @@
.vt-doc a[href^="https://sfc.vuejs.org"]:before
{
content: '▶';
width: 20px;
height: 20px;
display: inline-block;
border-radius: 10px;
vertical-align: middle;
position: relative;
top: -2px;
color: var(--vt-c-green);
border: 2px solid var(--vt-c-green);
margin-right: 8px;
margin-left: 4px;
line-height: 15px;
padding-left: 4.5px;
font-size: 11px;
}
.demo {
padding: 22px 24px;
border-radius: 8px;
box-shadow: var(--vt-shadow-2);
margin-bottom: 1.2em;
transition: background-color 0.5s ease;
}
.dark .demo {
background-color: var(--vt-c-bg-soft);
}
.demo p {
margin: 0;
}
.demo button {
background-color: var(--vt-c-bg-mute);
transition: background-color 0.5s;
padding: 5px 12px;
border: 1px solid var(--vt-c-divider);
border-radius: 8px;
font-size: 0.9em;
font-weight: 600;
}
.demo button + button {
margin-left: 1em;
}
.demo input,
.demo textarea,
.demo select {
border: 1px solid var(--vt-c-divider);
border-radius: 4px;
padding: 0.2em 0.6em;
margin-top: 10px;
background: transparent;
transition: background-color 0.5s;
}
.dark .demo select {
background: var(--vt-c-bg-soft);
}
.dark .demo select option {
background: transparent;
}
.demo input:not([type]):focus,
.demo textarea:focus,
.demo select:focus {
outline: 1px solid blue;
}
.demo select {
/* this was set by normalize.css */
-webkit-appearance: listbox;
}
.demo label {
margin: 0 1em 0 0.4em;
}
.demo select[multiple] {
width: 100px;
}
.demo h1 {
margin: 10px 0 0;
}

View File

@@ -0,0 +1,7 @@
.VPContent,
.VPContent .VPContentPage,
.VPContent .VPContentPage main,
.VPContent .VPContentPage main > div,
.VPContent .VPContentPage main > div > div {
@apply flex-1 flex flex-col;
}

View File

@@ -0,0 +1,27 @@
.next-steps {
margin-top: 3rem;
}
.next-steps .vt-box {
border: 1px solid var(--vt-c-bg-soft);
}
.next-steps .vt-box:hover {
border-color: var(--vt-c-green-light);
transition: border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.vt-doc .next-steps-link {
font-size: 20px;
line-height: 1.4;
letter-spacing: -0.02em;
margin-bottom: 0.75em;
display: block;
color: var(--vt-c-green);
}
.vt-doc .next-steps-caption {
margin-bottom: 0;
color: var(--vt-c-text-2);
transition: color 0.5s;
}

View File

@@ -0,0 +1,15 @@
/* always show anchors on /api/ and /style-guide/ pages */
.vt-doc.api h2 .header-anchor,
.vt-doc.style-guide h2 .header-anchor {
opacity: 1;
}
.vt-doc.sponsor h3 {
text-align: center;
padding-bottom: 1em;
border-bottom: 1px solid var(--vt-c-divider-light);
}
.vt-doc.sponsor h3 .header-anchor {
display: none;
}

View File

@@ -0,0 +1,61 @@
.style-example {
border-radius: 8px 8px 12px 12px;
margin: 1.6em 0;
padding: 1.6em 1.6em 0.1px;
position: relative;
border: 1px solid transparent;
transition: background-color 0.25s ease, border-color 0.25s ease;
}
.vt-doc .style-example h3 {
margin: 0;
font-size: 1.1em;
}
.style-example-bad {
background: #f7e8e8;
}
.dark .style-example-bad {
background: transparent;
border-color: var(--vt-c-red);
}
.style-example-bad h3 {
color: var(--vt-c-red);
}
.style-example-good {
background: #ecfaf7;
}
.dark .style-example-good {
background: transparent;
border-color: var(--vt-c-green);
}
.style-example-good h3 {
color: var(--vt-c-green);
}
.details summary {
font-weight: bold !important;
}
.style-verb {
font-size: 0.6em;
display: inline-block;
border-radius: 6px;
font-size: 0.65em;
line-height: 1;
font-weight: 600;
padding: 0.35em 0.4em 0.3em;
position: relative;
top: -0.15em;
margin-right: 0.5em;
color: var(--vt-c-bg);
transition: color 0.5s;
background-color: var(--vt-c-brand);
}
.style-verb.avoid {
background-color: var(--vt-c-red);
}

View File

@@ -0,0 +1,14 @@
.nowrap {
white-space: nowrap;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}

View File

@@ -0,0 +1 @@
export default ["6.x", "5.x", "4.x", "3.x"];

20
vendor/w7corp/easywechat/docs/README.md vendored Normal file
View File

@@ -0,0 +1,20 @@
# easywechat.com
## Contributing
This site is built with [VitePress](https://github.com/vuejs/vitepress) and depends on [@vue/theme](https://github.com/vuejs/vue-theme). Site content is written in Markdown format located in `src`. For simple edits, you can directly edit the file on GitHub and generate a Pull Request.
For local development, [pnpm](https://pnpm.io/) is preferred as package manager:
```bash
pnpm i
pnpm run dev
```
## Working on the content
- See VitePress docs on supported [Markdown Extensions](https://vitepress.vuejs.org/guide/markdown.html) and the ability to [use Vue syntax inside markdown](https://vitepress.vuejs.org/guide/using-vue.html).
## Working on the theme
If changes need to made for the theme, check out the [instructions for developing the theme alongside the docs](https://github.com/vuejs/vue-theme#developing-with-real-content).

13
vendor/w7corp/easywechat/docs/env.d.ts vendored Normal file
View File

@@ -0,0 +1,13 @@
/// <reference types="vitepress/client" />
/// <reference types="vue/macros-global" />
declare module '@overtrue/easywechat-theme/config' {
import { UserConfig } from 'vitepress'
const config: () => Promise<UserConfig>
export default config
}
declare module '@overtrue/easywechat-theme/highlight' {
const createHighlighter: () => Promise<(input: string) => string>
export default createHighlighter
}

View File

@@ -0,0 +1,34 @@
{
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"serve": "vitepress serve",
"preinstall": "npx only-allow pnpm"
},
"dependencies": {
"@overtrue/easywechat-theme": "^1.0.5",
"autoprefixer": "^10.4.7",
"clipboard": "^2.0.11",
"dynamics.js": "^1.1.5",
"gsap": "^3.10.4",
"postcss": "^8.4.14",
"postcss-import": "^14.1.0",
"tailwindcss": "^3.1.4",
"vitepress": "1.0.0-alpha.4",
"vue": "^3.2.37"
},
"devDependencies": {
"@types/markdown-it": "^12.2.3",
"@types/node": "^16.11.41"
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"@algolia/client-search",
"react",
"react-dom",
"@types/react"
]
}
}
}

1305
vendor/w7corp/easywechat/docs/pnpm-lock.yaml generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
module.exports = {
plugins: [
require('postcss-import'),
require("tailwindcss")("./tailwind.config.js"),
require("autoprefixer"),
require('postcss-nested')
]
}

View File

@@ -0,0 +1,59 @@
# Access Token
SDK 中有一个 [Access Token](https://github.com/overtrue/wechat/blob/master/src/Core/AccessToken.php) 对象,它是一个全局使用的东西,请把它与 OAuth 中的 code 换取的 Access Token 区别开。
我们一个 SDK 应用在初始化以后,你可以在任何时机从应用中拿到该配置下的 Access Token 实例:
```php
use EasyWeChat\Foundation\Application;
$options = [
//...
];
$app = new Application($options);
// 获取 access token 实例
$accessToken = $app->access_token; // EasyWeChat\Core\AccessToken 实例
$token = $accessToken->getToken(); // token 字符串
$token = $accessToken->getToken(true); // 强制重新从微信服务器获取 token.
```
## 修改 `$app` 的 Access Token
```php
$app['access_token']->setToken($newAccessToken, $expires);
```
例如:
```php
$app['access_token']->setToken('ccfdec35bd7ba359f6101c2da321d675');
// 或者指定过期时间
$app['access_token']->setToken('ccfdec35bd7ba359f6101c2da321d675', 3600); // 单位:秒
```
## 设置 AccessToken 的缓存
你也可以自定义 token 的缓存方式,把一个实现了 `Doctrine\Common\Cache\Cache` 缓存接口的实例作为 AccessToken 构造函数的第三个参数传入即可:
本项目使用 [doctrine/cache](https://github.com/doctrine/cache) 来完成缓存工作,它支持几乎目前所有的缓存引擎。
以 Redis 为例:
```php
use Doctrine\Common\Cache\RedisCache; // RedisCache 实例了 `Doctrine\Common\Cache\Cache` 接口
$cache = new RedisCache();
// 创建 redis 实例
$redis = new Redis();
$redis->connect('redis_host', 6379);
$cache->setRedis($redis);
$app->access_token->setCache($cache);
```

View File

@@ -0,0 +1,19 @@
# 账号接入
如果你想使用本项目接入多个公众号在本程序中您可以为每个帐号都设置一个id此id对应了该帐号的appid、token等信息。
如下表
| id | appId | secret | 其它... |
| --- | --- | --- | --- |
| 1 | `wx3cf0f39249eb0e60` | `f28f735d4f1c242f4687abb469072a29` | ... |
| 2 | `wx49eb0e63cf0f39s2` | `8f735d4687abb469f1c2422a29f4f207` | ... |
| N | `wx5cfeb0e60f392490` | `35f8f27d46f1c242f487a9072a29bb46` | ... |
在微信公众平台的设置中,您可以将您帐号中平台的 `url` 设置为 `您的网址/?id=xxx`,如:
```
http://www.example.com/wechat?id=1
```
而在程序入口处,根据 `id` 查找对应帐号的 `appid` 和 其它信息, 传入 'Overtrue\Wechat\Server',完成初始化。

View File

@@ -0,0 +1,67 @@
# 数据统计与分析
通过数据接口,开发者可以获取与公众平台官网统计模块类似但更灵活的数据,还可根据需要进行高级处理。
> 1. 接口侧的公众号数据的数据库中仅存储了 **2014年12月1日之后**的数据,将查询不到在此之前的日期,即使有查到,也是不可信的脏数据;
> 2. 请开发者在调用接口获取数据后,将数据保存在自身数据库中,即加快下次用户的访问速度,也降低了微信侧接口调用的不必要损耗。
> 3. 额外注意,获取图文群发每日数据接口的结果中,只有**中间页阅读人数+原文页阅读人数+分享转发人数+分享转发次数+收藏次数 >=3** 的结果才会得到统计,过小的阅读量的图文消息无法统计。
### 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
//...
$app = new Application($options);
$stats = $app->stats;
```
## API
$from example: `2014-02-13` 获取数据的起始日期
$to example: `2014-02-18` 获取数据的结束日期,`$to`允许设置的最大值为昨日
`$from``$to` 的差值需小于 “最大时间跨度”(比如最大时间跨度为 1 时,`$from``$to` 的差值只能为 0才能小于 1 ),否则会报错
+ `array userSummary($from, $to)` 获取用户增减数据, 最大时间跨度:**7**;
+ `array userCumulate($from, $to)` 获取累计用户数据, 最大时间跨度:**7**;
+ `array articleSummary($from, $to)` 获取图文群发每日数据, 最大时间跨度:**1**;
+ `array articleTotal($from, $to)` 获取图文群发总数据, 最大时间跨度:**1**;
+ `array userReadSummary($from, $to)` 获取图文统计数据, 最大时间跨度:**3**;
+ `array userReadHourly($from, $to)` 获取图文统计分时数据, 最大时间跨度:**1**;
+ `array userShareSummary($from, $to)` 获取图文分享转发数据, 最大时间跨度:**7**;
+ `array userShareHourly($from, $to)` 获取图文分享转发分时数据, 最大时间跨度:**1**;
+ `array upstreamMessageSummary($from, $to)` 获取消息发送概况数据, 最大时间跨度:**7**;
+ `array upstreamMessageHourly($from, $to)` 获取消息发送分时数据, 最大时间跨度:**1**;
+ `array upstreamMessageWeekly($from, $to)` 获取消息发送周数据, 最大时间跨度:**30**;
+ `array upstreamMessageMonthly($from, $to)` 获取消息发送月数据, 最大时间跨度:**30**;
+ `array upstreamMessageDistSummary($from, $to)` 获取消息发送分布数据, 最大时间跨度:**15**;
+ `array upstreamMessageDistWeekly($from, $to)` 获取消息发送分布周数据, 最大时间跨度:**30**;
+ `array upstreamMessageDistMonthly($from, $to)` 获取消息发送分布月数据, 最大时间跨度:**30**;
+ `array interfaceSummary($from, $to)` 获取接口分析数据, 最大时间跨度:**30**;
+ `array interfaceSummaryHourly($from, $to)` 获取接口分析分时数据, 最大时间跨度:**1**;
example:
```php
$userSummary = $stats->userSummary('2014-12-07', '2014-12-08');
var_dump($userSummary);
//
//[
// {
// "ref_date": "2014-12-07",
// "user_source": 0,
// "new_user": 0,
// "cancel_user": 0
// }
// //后续还有ref_date在begin_date和end_date之间的数据
// ]
```
更多详细内容与协议说明请查看微信官方文档http://mp.weixin.qq.com/wiki/ **数据统计** 章节

View File

@@ -0,0 +1,121 @@
# 群发
微信的群发消息接口有各种乱七八糟的注意事项及限制具体请阅读微信官方文档http://mp.weixin.qq.com/wiki/15/5380a4e6f02f2ffdc7981a8ed7a40753.html
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$broadcast = $app->broadcast;
```
## API
> 注意:
下面提到的 `$messageType``$message` 可以是:
- `$messageType = Broadcast::MSG_TYPE_NEWS;` 图文消息类型,所对应的 `$message` 为 media_id
- `$messageType = Broadcast::MSG_TYPE_TEXT;` 文本消息类型,所对应的 `$message` 为一个文本字符串
- `$messageType = Broadcast::MSG_TYPE_VOICE;` 语音消息类型,所对应的 `$message` 为 media_id
- `$messageType = Broadcast::MSG_TYPE_IMAGE;` 图片消息类型,所对应的 `$message` 为 media_id
- `$messageType = Broadcast::MSG_TYPE_CARD;` 卡券消息类型,所对应的 `$message` 为 card_id
- `$messageType = Broadcast::MSG_TYPE_VIDEO;` 视频消息为两种情况:
- 视频消息类型,群发视频消息给**组或预览群发视频消息**给用户时所对应的 `$message``media_id`
- 群发视频消息**给指定用户**时所对应的 `$message` 为一个数组 `['MEDIA_ID', 'TITLE', 'DESCRIPTION']`
### 群发消息给所有粉丝
```php
$broadcast->send($messageType, $message);
// 别名方式
$broadcast->sendText("大家好!欢迎使用 EasyWeChat。");
$broadcast->sendNews($mediaId);
$broadcast->sendVoice($mediaId);
$broadcast->sendImage($mediaId);
//视频:
// - 群发给组用户,或者预览群发视频时 $message 为 media_id
// - 群发给指定用户时为数组:[$media_Id, $title, $description]
$broadcast->sendVideo($message);
$broadcast->sendCard($cardId);
```
### 群发消息给指定组
```php
$broadcast->send($messageType, $message, $groupId);
// 别名方式
$broadcast->sendText($text, $groupId);
$broadcast->sendNews($mediaId, $groupId);
$broadcast->sendVoice($mediaId, $groupId);
$broadcast->sendImage($mediaId, $groupId);
$broadcast->sendVideo($message, $groupId);
$broadcast->sendCard($cardId, $groupId);
```
### 群发消息给指定用户
至少两个用户的openid必须是数组。
```php
$broadcast->send($messageType, $message, [$openId1, $openId2]);
// 别名方式
$broadcast->sendText($text, [$openId1, $openId2]);
$broadcast->sendNews($mediaId, [$openId1, $openId2]);
$broadcast->sendVoice($mediaId, [$openId1, $openId2]);
$broadcast->sendImage($mediaId, [$openId1, $openId2]);
$broadcast->sendVideo($message, [$openId1, $openId2]);
$broadcast->sendCard($cardId, [$openId1, $openId2]);
```
### 发送预览群发消息给指定的 `openId` 用户
```php
$broadcast->preview($messageType, $message, $openId);
// 别名方式
$broadcast->previewText($text, $openId);
$broadcast->previewNews($mediaId, $openId);
$broadcast->previewVoice($mediaId, $openId);
$broadcast->previewImage($mediaId, $openId);
$broadcast->previewVideo($message, $openId);
$broadcast->previewCard($cardId, $openId);
```
### 发送预览群发消息给指定的微信号用户
```php
$broadcast->previewByName($messageType, $message, $wxname);
// 别名方式
$broadcast->previewTextByName($text, $wxname);
$broadcast->previewNewsByName($mediaId, $wxname);
$broadcast->previewVoiceByName($mediaId, $wxname);
$broadcast->previewImageByName($mediaId, $wxname);
$broadcast->previewVideoByName($message, $wxname);
$broadcast->previewCardByName($cardId, $wxname);
```
### 删除群发消息
```php
$broadcast->delete($msgId);
```
### 查询群发消息发送状态
```php
$broadcast->status($msgId);
```
有关群发信息的更多细节请参考微信官方文档http://mp.weixin.qq.com/wiki/

View File

@@ -0,0 +1,118 @@
# 缓存
本项目使用 [doctrine/cache](https://github.com/doctrine/cache) 来完成缓存工作,它支持基本目前所有的缓存引擎。
在我们的 SDK 中的所有缓存默认使用文件缓存,缓存路径取决于 PHP 的临时目录,如果你需要自定义缓存,那么你需要做如下的事情:
你可以参考[doctrine/cache官方文档](http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/caching.html)来替换掉应用中默认的缓存配置:
> 以 redis 为例
> 请先安装 redis 拓展https://github.com/phpredis/phpredis
```php
use Doctrine\Common\Cache\RedisCache;
$cacheDriver = new RedisCache();
// 创建 redis 实例
$redis = new Redis();
$redis->connect('redis_host', 6379);
$cacheDriver->setRedis($redis);
$options = [
'debug' => false,
'app_id' => $wechatInfo['app_id'],
'secret' => $wechatInfo['app_secret'],
'token' => $wechatInfo['token'],
'aes_key' => $wechatInfo['aes_key'], // 可选
'cache' => $cacheDriver,
];
$wechatApp = new Application($options);
```
### Laravel 中使用
在 Laravel 中框架使用 [predis/predis](https://github.com/nrk/predis),那么我们就得使用 `Doctrine\Common\Cache\PredisCache`
```php
use Doctrine\Common\Cache\PredisCache;
$predis = app('redis')->connection();// connection($name), $name 默认为 `default`
$cacheDriver = new PredisCache($predis);
$app->cache = $cacheDriver;
```
> 上面提到的 `app('redis')->connection($name)`, 这里的 `$name` 是 laravel 项目中配置文件 `database.php` 中 `redis` 配置名 `default`https://github.com/laravel/laravel/blob/master/config/database.php#L118
> 如果你使用的其它连接,对应传名称就好了。
> 如果你在使用Laravel 5.4,应将`$predis = app('redis')->connection();`修改为:`$predis = app('redis')->connection()->client();`
## 使用自定义的缓存方式
如果你发现 doctrine 提供的几十种缓存方式都满足不了你的需求的话,那么你可以自己建立一个类来完成缓存操作,前提这个类得实现接口:[Doctrine\Common\Cache\Cache](https://github.com/doctrine/cache/blob/master/lib/Doctrine/Common/Cache/Cache.php)
该接口有以下方法需要实现:
```php
public function fetch($id); // 读取缓存
public function contains($id); // 检查是否存在缓存
public function save($id, $data, $lifeTime = 0); // 设置缓存
public function delete($id); // 删除缓存
public function getStats(); // 获取状态
```
下面为一个示例:
```php
<?php
use Doctrine\Common\Cache\Cache as CacheInterface;
class MyCacheDriver implements CacheInterface
{
public function fetch($id)
{
// 你自己从你想实现的存储方式读取并返回
}
public function contains($id)
{
// 同理 返回存在与否 bool 值
}
public function save($id, $data, $lifeTime = 0)
{
// 用你的方式存储该缓存内容即可
}
public function delete($id)
{
// 删除并返回 bool 值
}
public function getStats()
{
// 这个你可以不用实现,返回 null 即可
}
}
```
然后实例化你的缓存类并在 EasyWeChat 里使用它:
```php
$myCacheDriver = new MyCacheDriver();
$config = [
//...
'cache' => $myCacheDriver,
];
$wechatApp = new Application($options);
```
OK这样就完成了自定义缓存的操作。

View File

@@ -0,0 +1,801 @@
# 卡券
-
> Version `>=3.1.2`
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$card = $app->card;
```
## API列表
### 获取卡券颜色
```php
$card->getColors();
```
example:
```php
$result = $card->getColors();
```
### 创建卡券
创建卡券接口是微信卡券的基础接口用于创建一类新的卡券获取card_id创建成功并通过审核后商家可以通过文档提供的其他接口将卡券下发给用户每次成功领取库存数量相应扣除。
```php
$card->create($cardType, $baseInfo, $especial);
```
- `cardType` string - 是要添加卡券的类型
- `baseInfo` array - 为卡券的基本数据
- `especial` array - 是扩展字段
example:
```php
<?php
$cardType = 'GROUPON';
$baseInfo = [
'logo_url' => 'http://mmbiz.qpic.cn/mmbiz/2aJY6aCPatSeibYAyy7yct9zJXL9WsNVL4JdkTbBr184gNWS6nibcA75Hia9CqxicsqjYiaw2xuxYZiaibkmORS2oovdg/0',
'brand_name' => '测试商户造梦空间',
'code_type' => 'CODE_TYPE_QRCODE',
'title' => '测试',
'sub_title' => '测试副标题',
'color' => 'Color010',
'notice' => '测试使用时请出示此券',
'service_phone' => '15311931577',
'description' => "测试不可与其他优惠同享\n如需团购券发票,请在消费时向商户提出\n店内均可使用,仅限堂食",
'date_info' => [
'type' => 'DATE_TYPE_FIX_TERM',
'fixed_term' => 90, //表示自领取后多少天内有效不支持填写0
'fixed_begin_term' => 0, //表示自领取后多少天开始生效领取后当天生效填写0。
],
'sku' => [
'quantity' => '0', //自定义code时设置库存为0
],
'location_id_list' => ['461907340'], //获取门店位置poi_id具备线下门店的商户为必填
'get_limit' => 1,
'use_custom_code' => true, //自定义code时必须为true
'get_custom_code_mode' => 'GET_CUSTOM_CODE_MODE_DEPOSIT', //自定义code时设置
'bind_openid' => false,
'can_share' => true,
'can_give_friend' => false,
'center_title' => '顶部居中按钮',
'center_sub_title' => '按钮下方的wording',
'center_url' => 'http://www.qq.com',
'custom_url_name' => '立即使用',
'custom_url' => 'http://www.qq.com',
'custom_url_sub_title' => '6个汉字tips',
'promotion_url_name' => '更多优惠',
'promotion_url' => 'http://www.qq.com',
'source' => '造梦空间',
];
$especial = [
'deal_detail' => 'deal_detail',
];
$result = $card->create($cardType, $baseInfo, $especial);
```
### 创建二维码
开发者可调用该接口生成一张卡券二维码供用户扫码后添加卡券到卡包。
自定义Code码的卡券调用接口时POST数据中需指定code非自定义code不需指定指定openid同理。指定后的二维码只能被用户扫描领取一次。
```php
$card->QRCode($cards);
```
- `cards` array - 卡券相关信息
example:
```php
//领取单张卡券
$cards = [
'action_name' => 'QR_CARD',
'expire_seconds' => 1800,
'action_info' => [
'card' => [
'card_id' => 'pdkJ9uFS2WWCFfbbEfsAzrzizVyY',
'is_unique_code' => false,
'outer_id' => 1,
],
],
];
$result = $card->QRCode($cards);
```
```php
//领取多张卡券
$cards = [
'action_name' => 'QR_MULTIPLE_CARD',
'action_info' => [
'multiple_card' => [
'card_list' => [
['card_id' => 'pdkJ9uFS2WWCFfbbEfsAzrzizVyY'],
],
],
],
];
$result = $card->QRCode($cardList);
```
请求成功返回值示例:
```php
array(4) {
["ticket"]=>
string(96) "gQHa7joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xLzdrUFlQMHJsV3Zvanc5a2NzV1N5AAIEJUVyVwMEAKd2AA=="
["expire_seconds"]=>
int(7776000)
["url"]=>
string(43) "http://weixin.qq.com/q/7kPYP0rlWvojw9kcsWSy"
["show_qrcode_url"]=>
string(151) "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=gQHa7joAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xLzdrUFlQMHJsV3Zvanc5a2NzV1N5AAIEJUVyVwMEAKd2AA%3D%3D"
}
```
成功返回值列表说明:
| 参数名 | 描述 |
| :-------------: | :--------------------------------------- |
| ticket | 获取的二维码ticket凭借此ticket调用[通过ticket换取二维码接口](http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1443433542&token=&lang=zh_CN)可以在有效时间内换取二维码。 |
| expire_seconds | 二维码的有效时间 |
| url | 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片 |
| show_qrcode_url | 二维码显示地址,点击后跳转二维码页面 |
### ticket 换取二维码图片
获取二维码 ticket 后,开发者可用 ticket 换取二维码图片。
```php
$card->showQRCode($ticket);
```
- `ticket` string - 获取的二维码 ticket凭借此 ticket 可以在有效时间内换取二维码。
example:
```php
$ticket = 'gQFF8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL01VTzN0T0hsS1BwUlBBYUszbVN5AAIEughxVwMEAKd2AA==';
$result = $card->showQRCode($ticket);
```
### ticket 换取二维码链接
```php
$card->getQRCodeUrl($ticket); //获取的二维码ticket
```
example:
```php
$ticket = 'gQFF8DoAAAAAAAAAASxodHRwOi8vd2VpeGluLnFxLmNvbS9xL01VTzN0T0hsS1BwUlBBYUszbVN5AAIEughxVwMEAKd2AA==';
$card->getQRCodeUrl($ticket);
```
### JSAPI 卡券批量下发到用户
微信卡券JSAPI 卡券
```php
$cards = [
['card_id' => 'pdkJ9uLRSbnB3UFEjZAgUxAJrjeY', 'outer_id' => 2],
['card_id' => 'pdkJ9uJ37aU-tyRj4_grs8S45k1c', 'outer_id' => 3],
];
$json = $card->jsConfigForAssign($cards); // 返回 json 格式
```
返回 json在模板里的用法
```html
wx.addCard({
cardList: <?= $json ?>, // 需要打开的卡券列表
success: function (res) {
var cardList = res.cardList; // 添加的卡券列表信息
}
});
```
### 创建货架接口
开发者需调用该接口创建货架链接,用于卡券投放。创建货架时需填写投放路径的场景字段。
```php
$card->createLandingPage($banner, $pageTitle, $canShare, $scene, $cards);
```
- `banner` string -页面的 banner 图;
- `pageTitle` string - 页面的 title
- `canShare` bool - 页面是不是可以分享true 或 false
- `scene` string - 投放页面的场景值,具体值请参考下面的 example
- `cards` array - 卡券列表,每个元素有两个字段
example:
```php
$banner = 'http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFN';
$pageTitle = '惠城优惠大派送';
$canShare = true;
//SCENE_NEAR_BY 附近
//SCENE_MENU 自定义菜单
//SCENE_QRCODE 二维码
//SCENE_ARTICLE 公众号文章
//SCENE_H5 h5页面
//SCENE_IVR 自动回复
//SCENE_CARD_CUSTOM_CELL 卡券自定义cell
$scene = 'SCENE_NEAR_BY';
$cardList = [
['card_id' => 'pdkJ9uLRSbnB3UFEjZAgUxAJrjeY', 'thumb_url' => 'http://test.digilinx.cn/wxApi/Uploads/test.png'],
['card_id' => 'pdkJ9uJ37aU-tyRj4_grs8S45k1c', 'thumb_url' => 'http://test.digilinx.cn/wxApi/Uploads/aa.jpg'],
];
$result = $card->createLandingPage($banner, $pageTitle, $canShare, $scene, $cardList);
```
### 导入code接口
在自定义code卡券成功创建并且通过审核后必须将自定义code按照与发券方的约定数量调用导入code接口导入微信后台。
```php
$card->deposit($card_id, $code);
```
- `cardId` string - 要导入code的卡券ID
- `code` string - 要导入微信卡券后台的自定义 code最多100个
example:
```php
$cardId = 'pdkJ9uLCEF_HSKO7JdQOUcZ-PUzo';
$code = ['11111', '22222', '33333'];
$result = $card->deposit($cardId, $code);
```
### 查询导入code数目
```php
$card->getDepositedCount($cardId); //要导入code的卡券ID
```
example:
```php
$cardId = 'pdkJ9uLCEF_HSKO7JdQOUcZ-PUzo';
$result = $card->getDepositedCount($cardId);
```
### 核查code接口
为了避免出现导入差错强烈建议开发者在查询完code数目的时候核查code接口校验code导入微信后台的情况。
```php
$card->checkCode($cardId, $code);
```
example:
```php
$cardId = 'pdkJ9uLCEF_HSKO7JdQOUcZ-PUzo';
$code = ['807732265476', '22222', '33333'];
$result = $card->checkCode($cardId, $code);
```
### 图文消息群发卡券
特别注意目前该接口仅支持填入非自定义code的卡券,自定义code的卡券需先进行code导入后调用。
```php
$card->getHtml($cardId);
```
example:
```php
$cardId = 'pdkJ9uLCEF_HSKO7JdQOUcZ-PUzo';
$result = $card->getHtml($cardId);
```
### 设置测试白名单
同时支持“openid”、“username”两种字段设置白名单总数上限为10个。
```php
$card->setTestWhitelist($openids); // 使用 openid
$card->setTestWhitelistByUsername($usernames); // 使用 username
```
- `openids` array - 测试的openid列表
- `usernames` array - 测试的微信号列表
example:
```php
// by openid
$openids = [$openId, $openId2, $openid3...];
$result = $card->setTestWhitelist($openids);
// by username
$usernames = ['tianye0327', 'iovertrue'];
$result = $card->setTestWhitelistByUsername($usernames);
```
### 查询Code接口
```php
$card->getCode($code, $checkConsume, $cardId);
```
- checkConsume 是否校验code核销状态true和false
example:
```php
$code = '736052543512';
$checkConsume = true;
$cardId = 'pdkJ9uDgnm0pKfrTb1yV0dFMO_Gk';
$result = $card->getCode($code, $checkConsume, $cardId);
```
### 核销Code接口
```php
$card->consume($code);
// 或者指定 cardId
$card->consume($code, $cardId);
```
example:
```php
$cardId = 'pdkJ9uDmhkLj6l5bm3cq9iteQBck';
$code = '789248558333';
$result = $card->consume($code);
//或
$result = $card->consume($code, $cardId);
```
### Code解码接口
```php
$card->decryptCode($encryptedCode);
```
example:
```php
$encryptedCode = 'XXIzTtMqCxwOaawoE91+VJdsFmv7b8g0VZIZkqf4GWA60Fzpc8ksZ/5ZZ0DVkXdE';
$result = $card->decryptCode($encryptedCode);
```
### 获取用户已领取卡券接口
用于获取用户卡包里的属于该appid下所有**可用卡券,包括正常状态和未生效状态**。
```php
$card->getUserCards($openid, $cardId);
```
example:
```php
$openid = 'odkJ9uDUz26RY-7DN1mxkznfo9xU';
$cardId = ''; //卡券ID。不填写时默认查询当前appid下的卡券。
$result = $card->getUserCards($openid, $cardId);
```
### 查看卡券详情
开发者可以调用该接口查询某个card_id的创建信息、审核状态以及库存数量。
```php
$card->getCard($cardId);
```
example:
```php
$cardId = 'pdkJ9uLRSbnB3UFEjZAgUxAJrjeY';
$result = $card->getCard($cardId);
```
### 批量查询卡列表
```php
$card->lists($offset, $count, $statusList);
```
- `offset` int - 查询卡列表的起始偏移量从0开始
- `count` int - 需要查询的卡片的数量
- `statusList` - 支持开发者拉出指定状态的卡券列表详见example
example:
```php
$offset = 0;
$count = 10;
//CARD_STATUS_NOT_VERIFY,待审核;
//CARD_STATUS_VERIFY_FAIL,审核失败;
//CARD_STATUS_VERIFY_OK通过审核
//CARD_STATUS_USER_DELETE卡券被商户删除
//CARD_STATUS_DISPATCH在公众平台投放过的卡券
$statusList = 'CARD_STATUS_VERIFY_OK';
$result = $card->lists($offset, $count, $statusList);
```
### 更改卡券信息接口
支持更新所有卡券类型的部分通用字段及特殊卡券中特定字段的信息。
```php
$card->update($cardId, $type, $baseInfo);
```
- `type` string - 卡券类型
example:
```php
$cardId = 'pdkJ9uCzKWebwgNjxosee0ZuO3Os';
$type = 'groupon';
$baseInfo = [
'logo_url' => 'http://mmbiz.qpic.cn/mmbiz/2aJY6aCPatSeibYAyy7yct9zJXL9WsNVL4JdkTbBr184gNWS6nibcA75Hia9CqxicsqjYiaw2xuxYZiaibkmORS2oovdg/0',
'center_title' => '顶部居中按钮',
'center_sub_title' => '按钮下方的wording',
'center_url' => 'http://www.baidu.com',
'custom_url_name' => '立即使用',
'custom_url' => 'http://www.qq.com',
'custom_url_sub_title' => '6个汉字tips',
'promotion_url_name' => '更多优惠',
'promotion_url' => 'http://www.qq.com',
];
$result = $card->update($cardId, $type, $baseInfo);
```
### 设置微信买单接口
```php
$card->setPayCell($cardId, $isOpen);
```
- `isOpen` string - 是否开启买单功能,填 true/false不填默认 true
example:
```php
$cardId = 'pdkJ9uH7u11R-Tu1kilbaW_zDFow';
$isOpen = true;
$result = $card->setPayCell($cardId, $isOpen);
```
### 修改库存接口
```php
$card->increaseStock($cardId, $amount); // 增加库存
$card->reductStock($cardId, $amount); // 减少库存
```
- `cardId` string - 卡券 ID
- `amount` int - 修改多少库存
example:
```php
$cardId = 'pdkJ9uLRSbnB3UFEjZAgUxAJrjeY';
$result = $card->increaseStock($cardId, 100);
```
### 更改Code接口
```php
$card->updateCode($code, $newCode, $cardId);
```
- `newCode` string - 变更后的有效Code码
example:
```php
$code = '148246271394';
$newCode = '659266965266';
$cardId = '';
$result = $card->updateCode($code, $newCode, $cardId);
```
### 删除卡券接口
```php
$card->delete($cardId);
```
example:
```php
$cardId = 'pdkJ9uItT7iUpBp4GjZp8Cae0Vig';
$result = $card->delete($cardId);
```
### 设置卡券失效
```php
$card->disable($code, $cardId);
```
example:
```php
$code = '736052543512';
$cardId = '';
$result = $card->disable($code, $cardId);
```
### 会员卡接口激活
```php
$result = $card->activate($info);
```
- `info` - 需要激活的会员卡信息
example:
```php
$activate = [
'membership_number' => '357898858', //会员卡编号由开发者填入作为序列号显示在用户的卡包里。可与Code码保持等值。
'code' => '916679873278', //创建会员卡时获取的初始code。
'activate_begin_time' => '1397577600', //激活后的有效起始时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式
'activate_end_time' => '1422724261', //激活后的有效截至时间。若不填写默认以创建时的 data_info 为准。Unix时间戳格式。
'init_bonus' => '持白金会员卡到店消费可享8折优惠。', //初始积分不填为0。
'init_balance' => '持白金会员卡到店消费可享8折优惠。', //初始余额不填为0。
'init_custom_field_value1' => '白银', //创建时字段custom_field1定义类型的初始值限制为4个汉字12字节。
'init_custom_field_value2' => '9折', //创建时字段custom_field2定义类型的初始值限制为4个汉字12字节。
'init_custom_field_value3' => '200', //创建时字段custom_field3定义类型的初始值限制为4个汉字12字节。
];
$result = $card->activate($activate);
```
### 设置开卡字段接口
```php
$card->activateUserForm($cardId, $requiredForm, $optionalForm);
```
- `requiredForm` array - 会员卡激活时的必填选项
- `optionalForm` array - 会员卡激活时的选填项
example:
```php
$cardId = 'pdkJ9uJYAyfLXsUCwI2LdH2Pn1AU';
$requiredForm = [
'required_form' => [
'common_field_id_list' => [
'USER_FORM_INFO_FLAG_MOBILE',
'USER_FORM_INFO_FLAG_LOCATION',
'USER_FORM_INFO_FLAG_BIRTHDAY',
],
'custom_field_list' => [
'喜欢的食物',
],
],
];
$optionalForm = [
'optional_form' => [
'common_field_id_list' => [
'USER_FORM_INFO_FLAG_EMAIL',
],
'custom_field_list' => [
'喜欢的食物',
],
],
];
$result = $card->activateUserForm($cardId, $requiredForm, $optionalForm);
```
### 拉取会员信息接口
```php
$card->getMemberCardUser($cardId, $code);
```
example:
```php
$cardId = 'pbLatjtZ7v1BG_ZnTjbW85GYc_E8';
$code = '916679873278';
$result = $card->getMemberCardUser($cardId, $code);
```
### 更新会员信息
```php
$card->updateMemberCardUser($updateUser);
```
- `updateUser` array - 可以更新的会员信息
example:
```php
$updateUser = [
'code' => '916679873278', //卡券Code码。
'card_id' => 'pbLatjtZ7v1BG_ZnTjbW85GYc_E8', //卡券ID。
'record_bonus' => '消费30元获得3积分', //商家自定义积分消耗记录不超过14个汉字。
'bonus' => '100', //需要设置的积分全量值传入的数值会直接显示如果同时传入add_bonus和bonus,则前者无效。
'balance' => '持白金会员卡到店消费可享8折优惠。', //需要设置的余额全量值传入的数值会直接显示如果同时传入add_balance和balance,则前者无效。
'record_balance' => '持白金会员卡到店消费可享8折优惠。', //商家自定义金额消耗记录不超过14个汉字。
'custom_field_value1' => '100', //创建时字段custom_field1定义类型的最新数值限制为4个汉字12字节。
'custom_field_value2' => '200', //创建时字段custom_field2定义类型的最新数值限制为4个汉字12字节。
'custom_field_value3' => '300', //创建时字段custom_field3定义类型的最新数值限制为4个汉字12字节。
];
$result = $card->updateMemberCardUser($updateUser);
```
### 添加子商户
```php
$card->craeteSubMerchant($brandName, $logoUrl, $protocol, $endTime, $primaryCategoryId, $secondaryCategoryId, $agreementMediaId, $operatorMediaId, $appId); 
```
- `brand_name` string - 子商户名称12个汉字内该名称将在制券时填入并显示在卡券页面上
- `logo_url` string - 子商户 logo可通过上传 logo 接口获取。该 logo 将在制券时填入并显示在卡券页面上
- `protocol` string - 授权函ID即通过上传临时素材接口上传授权函后获得的 meida_id
- `primary_category_id` int - 一级类目id,可以通过本文档中接口查询
- `secondary_category_id` int - 二级类目id可以通过本文档中接口查询
- `agreement_media_id` string - 营业执照或个体工商户营业执照彩照或扫描件
- `operator_media_id` string - 营业执照内登记的经营者身份证彩照或扫描件
- `app_id` string - 子商户的公众号 app_id配置后子商户卡券券面上的 app_id 为该 app_id, app_id 须经过认证
example:
```php
$info = [
'brand_name' => 'overtrue',
'logo_url' => 'http://mmbiz.qpic.cn/mmbiz/iaL1LJM1mF9aRKPZJkmG8xXhiaHqkKSVMMWeN3hLut7X7hicFNjakmxibMLGWpXrEXB33367o7zHN0CwngnQY7zb7g/0',
'protocol' => 'qIqwTfzAdJ_1-VJFT0fIV53DSY4sZY2WyhkzZzbV498Qgdp-K5HJtZihbHLS0Ys0',
'end_time' => '1438990559',
'primary_category_id' => 1,
'secondary_category_id' => 101,
'agreement_media_id' => '',
'operator_media_id' => '',
'app_id' => '',
];
$result = $card->createSubMerchant($info);
```
### 更新子商户
```php
$card->updateSubMerchant($merchantId, $info);
```
- `$merchantId` int - 子商户 ID
- `$info` array - 参数与创建子商户参数一样
example:
```php
$info = [
//...
];
$result = $card->updateSubMerchant('12', $info);
```
### 卡券开放类目查询接口
```php
$card->getCategories();
```
example:
```php
$result = $card->getCategories();
```
关于卡券接口的使用请参阅官方文档http://mp.weixin.qq.com/wiki/

View File

@@ -0,0 +1,107 @@
# 配置
在前面我们已经讲过,初始化 SDK 的时候方法就是创建一个 `EasyWeChat\Foundation\Application` 实例:
```php
use EasyWeChat\Foundation\Application;
$options = [
// ...
];
$app = new Application($options);
/**
* 如果想要在Application实例化完成之后, 修改某一个options的值,
* 比如服务商+子商户支付回调场景, 所有子商户订单支付信息都是通过同一个服务商的$option 配置进来的,
* 当oauth在微信端验证完成之后, 可以通过动态设置merchant_id来区分具体是哪个子商户
*/
$app['config']->set('oauth.callback','wechat/oauthcallback/'. $sub_merchant_id->id);
```
那么配置的具体选项有哪些,下面是一个完整的列表:
```php
<?php
return [
/**
* Debug 模式bool 值true/false
*
* 当值为 false 时,所有的日志都不会记录
*/
'debug' => true,
/**
* 账号基本信息,请从微信公众平台/开放平台获取
*/
'app_id' => 'your-app-id', // AppID
'secret' => 'your-app-secret', // AppSecret
'token' => 'your-token', // Token
'aes_key' => '', // EncodingAESKey安全模式与兼容模式下请一定要填写
/**
* 日志配置
*
* level: 日志级别, 可选为:
* debug/info/notice/warning/error/critical/alert/emergency
    * permission日志文件权限(可选)默认为null若为null值,monolog会取0644
    * file日志文件位置(绝对路径!!!),要求可写权限
*/
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => '/tmp/easywechat.log',
],
/**
* OAuth 配置
*
* scopes公众平台snsapi_userinfo / snsapi_base开放平台snsapi_login
* callbackOAuth授权完成后的回调页地址
*/
'oauth' => [
'scopes' => ['snsapi_userinfo'],
'callback' => '/examples/oauth_callback.php',
],
/**
* 微信支付
*/
'payment' => [
'merchant_id' => 'your-mch-id',
'key' => 'key-for-signature',
'cert_path' => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
'key_path' => 'path/to/your/key', // XXX: 绝对路径!!!!
// 'device_info' => '013467007045764',
// 'sub_app_id' => '',
// 'sub_merchant_id' => '',
// ...
],
/**
* Guzzle 全局设置
*
* 更多请参考: http://docs.guzzlephp.org/en/latest/request-options.html
*/
'guzzle' => [
'timeout' => 3.0, // 超时时间(秒)
//'verify' => false, // 关掉 SSL 认证(强烈不建议!!!)
],
];
```
> :heart: 安全模式下请一定要填写 `aes_key`
## 日志文件
配置文件里的`/tmp/...`是绝对路径
如果在 windows 下,去把它改成`C:\foo\bar`的形式,
如果是 Linux ,你已经懂了……
如果需要按日独立存储,可以配置成`'file' => storage_path('/tmp/easywechat/easywechat_'.date('Ymd').'.log'),`
其它同理……

View File

@@ -0,0 +1,56 @@
# 贡献代码
## 开发
我们欢迎广大开发者贡献大家的智慧,让我们共同让它变得更完美.
### 开始之前
请严格遵循以下代码标准:
- [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
- 使用 4 个空格作为缩进。
### 流程
1. Fork [overtrue/wechat](https://github.com/overtrue/wechat) 到本地.
2. 创建新的分支:
```bash
git checkout -b new_feature
```
3. 编写代码。
4. Push 到你的分支:
```bash
git push origin new_feature
```
5. 创建 Pull Request 并描述你完成的功能或者做出的修改。
> 注意:注释请使用英文
## 更新文档
我们的文档也是开源的,源代码在 [w7corp/EasyWeChat/docs](https://github.com/w7corp/easywechat/tree/master/docs)。
### 流程
1. Fork [w7corp/EasyWeChat](https://github.com/w7corp/EasyWeChat)
2. Clone 到你的电脑:
```bash
git clone https://github.com/[你的账号]/EasyWeChat
cd docs
```
3. 创建新的分支,编辑文档
4. Push 到你的分支。
5. 创建 Pull Request 并描述你完成的功能或者做出的修改。
## 报告 Bug
当你在使用过程中遇到问题,请查阅 [疑难解答](troubleshooting.html) 或者在这里提问 [GitHub](https://github.com/overtrue/wechat/issues). 如果还是不能解决你的问题,请到 GitHub 联系我们。
[overtrue/wechat]: https://github.com/overtrue/wechat

View File

@@ -0,0 +1,44 @@
# 事件
> 注意3.0 起,所有服务端的入口(**消息与事件**)都已经合并为一个方法来处理:`setMessageHandler()`
### 在服务端接收用户端产生的事件
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$server = $app->server;
$server->setMessageHandler(function($message){
// 注意,这里的 $message 不仅仅是用户发来的消息,也可能是事件
// 当 $message->MsgType 为 event 时为事件
if ($message->MsgType == 'event') {
# code...
switch ($message->Event) {
case 'subscribe':
# code...
break;
default:
# code...
break;
}
}
});
$response = $server->serve();
$response->send(); // Laravel 里请使用return $response;
```
> 注意:`$response` 是一个对象,不要直接 echo.
更多请参考:[服务端](server.html)
关于事件类型请参考微信官方文档http://mp.weixin.qq.com/wiki/

View File

@@ -0,0 +1,23 @@
> 👋🏼 您当前浏览的文档为 3.x其它版本的文档请参考[6.x](/6.x/)、[5.x](/5.x/)、[4.x](/4.x/)
# EasyWeChat
EasyWeChat 是一个开源的 [微信](http://www.wechat.com) 非官方 SDK。安装非常简单因为它是一个标准的 [Composer](https://getcomposer.org/) 包,这意味着任何满足下列安装条件的 PHP 项目支持 Composer 都可以使用它。
## 环境要求
- PHP >= 5.5.9
- [PHP cURL 扩展](http://php.net/manual/en/book.curl.php)
- [PHP OpenSSL 扩展](http://php.net/manual/en/book.openssl.php)
- [PHP fileinfo 拓展](http://php.net/manual/en/book.fileinfo.php)
# 参与贡献
1. fork 当前库到你的名下
2. 切换到你想要修改的分支,`zh-cn` 或者 `en`
3. 在你的本地修改完成审阅过后提交到你的仓库的对应分支
4. 提交 PR 并描述你的修改,等待合并
# License
MIT

View File

@@ -0,0 +1,20 @@
# 安装
## 环境要求
- PHP >= 5.5.9
- [PHP cURL 扩展](http://php.net/manual/en/book.curl.php)
- [PHP OpenSSL 扩展](http://php.net/manual/en/book.openssl.php)
- [PHP fileinfo 拓展](http://php.net/manual/en/book.fileinfo.php) 素材管理模块需要用到
Laravel 5 拓展包: [overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
## 安装
使用 [composer](http://getcomposer.org/):
```shell
$ composer require overtrue/wechat:~3.1 -vvv
```

View File

@@ -0,0 +1,27 @@
# 在框架中使用
EasyWeChat 是一个通用的 Composer 包,所以不需要对框架单独做修改,只要支持 Composer 就能直接使用,当然了,为了更方便的使用,我们收集了以下框架单独提供的拓展包:
## Laravel
- [overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
## Symfony
- [lilocon/WechatBundle](https://github.com/lilocon/WechatBundle)
## Yii
- [max-wen/yii2-easy-wechat](https://github.com/max-wen/yii2-easy-wechat)
## CI
TODO
## Phalcon
TODO
... more

View File

@@ -0,0 +1,46 @@
# JSSDK
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
//...
$app = new Application($options);
$js = $app->js;
```
## API
- `$js->config(array $APIs, $debug = false, $beta = false, $json = true);` 获取JSSDK的配置数组默认返回 JSON 字符串,当 `$json``false` 时返回数组,你可以直接使用到网页中。
- `$js->setUrl($url)` 设置当前URL如果不想用默认读取的URL可以使用此方法手动设置通常不需要。
example:
我们可以生成js配置文件
```js
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
wx.config(<?php echo $js->config(array('onMenuShareQQ', 'onMenuShareWeibo'), true) ?>);
</script>
```
结果如下:
```js
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
wx.config({
debug: true,
appId: 'wx3cf0f39249eb0e60',
timestamp: 1430009304,
nonceStr: 'qey94m021ik',
signature: '4F76593A4245644FAE4E1BC940F6422A0C3EC03E',
jsApiList: ['onMenuShareQQ', 'onMenuShareWeibo']
});
</script>
```
更多 JSSDK 的使用请参考 [微信官方文档](http://mp.weixin.qq.com/wiki/) 中 **JSSDK章节**

View File

@@ -0,0 +1,142 @@
# 红包
你在阅读本文之前确认你已经仔细阅读了:[微信支付 | 现金红包文档 ](https://pay.weixin.qq.com/wiki/doc/api/tools/cash_coupon.php?chapter=13_1)。
## 配置
与支付接口一样,红包接口也需要配置如下参数,需要特别注意的是,红包相关的全部接口**都需要使用 SSL 证书**,因此** cert_path 以及 cert_key 必须正确配置**。
```php
<?php
use EasyWeChat\Foundation\Application;
$options = [
// payment
'payment' => [
'merchant_id' => 'your-mch-id',
'key' => 'key-for-signature',
'cert_path' => 'path/to/your/cert.pem',
'key_path' => 'path/to/your/key',
// ...
],
];
$app = new Application($options);
$luckyMoney = $app->lucky_money;
```
## 发送红包
微信的现金红包分为**普通红包**和**裂变红包**两类。SDK 中对其分别进行了封装,同时也提供了一个统一的调用方法。
**默认情况下通过接口发送的红包金额应该在200元以内但可以通过在调用发送接口时传递场景 ID (scene_id)来发送特定场景的红包不同场景红包可以由商户自己登录商户平台设置最大金额。scene_id 的可选值及对应含义可参阅微信支付官方文档。**
### 通用发送接口
```php
<?php
$luckyMoneyData = [
'mch_billno' => 'xy123456',
'send_name' => '测试红包',
're_openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'total_num' => 1, //普通红包固定为1裂变红包不小于3
'total_amount' => 100, //单位为分普通红包不小于100裂变红包不小于300
'wishing' => '祝福语',
'client_ip' => '192.168.0.1', //可不传,不传则由 SDK 取当前客户端 IP
'act_name' => '测试活动',
'remark' => '测试备注',
// ...
];
$result = $luckyMoney->send($luckyMoneyData, \EasyWeChat\Payment\LuckyMoney\API::TYPE_NORMAL);
$result = $luckyMoney->send($luckyMoneyData, \EasyWeChat\Payment\LuckyMoney\API::TYPE_GROUP);
```
> 不同类型红包所传参数有所差别,请参考官方文档中参数列表。
### 发送普通红包接口
```php
<?php
$luckyMoneyData = [
'mch_billno' => 'xy123456',
'send_name' => '测试红包',
're_openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'total_num' => 1, //固定为1可不传
'total_amount' => 100, //单位为分不小于100
'wishing' => '祝福语',
'client_ip' => '192.168.0.1', //可不传,不传则由 SDK 取当前客户端 IP
'act_name' => '测试活动',
'remark' => '测试备注',
// ...
];
$result = $luckyMoney->sendNormal($luckyMoneyData);
```
### 发送裂变红包接口
```php
<?php
$luckyMoneyData = [
'mch_billno' => 'xy123456',
'send_name' => '测试红包',
're_openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'total_num' => 3, //不小于3
'total_amount' => 300, //单位为分不小于300
'wishing' => '祝福语',
'act_name' => '测试活动',
'remark' => '测试备注',
'amt_type' => 'ALL_RAND', //可不传
// ...
];
$result = $luckyMoney->sendGroup($luckyMoneyData);
```
## 红包预下单接口
红包预下单接口是为摇一摇红包接口配合使用的,在开发摇一摇周边的摇红包相关功能时,需要调用本接口获取红包单号。详情参见[官方文档](http://mp.weixin.qq.com/wiki/7/0ddd50ed2421b99fedd071281c074aab.html#.E7.BA.A2.E5.8C.85.E9.A2.84.E4.B8.8B.E5.8D.95.E6.8E.A5.E5.8F.A3)
```php
<?php
$luckyMoneyData = [
'hb_type' => 'NORMAL', //NORMAL 或 GROUP
'mch_billno' => 'xy123456',
'send_name' => '测试红包',
're_openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'total_num' => 1, //普通红包固定为1裂变红包不小于3
'total_amount' => 100, //单位为分普通红包不小于100裂变红包不小于300
'wishing' => '祝福语',
'client_ip' => '192.168.0.1', //可不传,不传则由 SDK 取当前客户端 IP
'act_name' => '测试活动',
'remark' => '测试备注',
'amt_type' => 'ALL_RAND',
// ...
];
$result = $luckyMoney->prepare($luckyMoneyData);
```
## 查询红包信息
用于商户对已发放的红包进行查询红包的具体信息以及领取情况 ,普通红包和裂变包均使用这一接口进行查询。
```php
$mchBillNo = "商户系统内部的订单号mch_billno";
$luckyMoney->query($mchBillNo);
```

View File

@@ -0,0 +1,329 @@
# 素材管理
在微信里的图片,音乐,视频等等都需要先上传到微信服务器作为素材才可以在消息中使用。
> 请注意:
> 1. 限制:
> - 图片image: 1M支持 bmp/png/jpeg/jpg/gif 格式
> - 语音voice2M播放长度不超过 60s支持 mp3/wma/wav/amr 格式
> - 视频video10MB支持MP4格式
> - 缩略图thumb64KB支持JPG格式
> 2. `media_id` 是可复用的;
> 3. 素材分为 `临时素材` 与 `永久素材` 临时素材媒体文件在后台保存时间为3天即 3 天后 `media_id` 失效;
> 4. 新增的永久素材也可以在公众平台官网素材管理模块中看到;
> 5. 永久素材的数量是有上限的请谨慎新增。图文消息素材和图片素材的上限为5000其他类型为1000
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
$app = new Application($options);
// 永久素材
$material = $app->material;
// 临时素材
$temporary = $app->material_temporary;
```
## 永久素材 API
### 上传图片:
> 注意:微信图片上传服务有敏感检测系统,图片内容如果含有敏感内容,如色情,商品推广,虚假信息等,上传可能失败。
```php
$result = $material->uploadImage("/path/to/your/image.jpg"); // 请使用绝对路径写法!除非你正确的理解了相对路径(好多人是没理解对的)!
var_dump($result);
// {
// "media_id":MEDIA_ID,
// "url":URL
// }
```
> `url` 只有上传图片素材有返回值。
### 上传声音
语音**大小不超过 5M****长度不超过 60 秒**,支持 `mp3/wma/wav/amr` 格式。
```php
$result = $material->uploadVoice("/path/to/your/voice.mp3"); // 请使用绝对路径写法!除非你正确的理解了相对路径(好多人是没理解对的)!
$mediaId = $result->media_id;
// {
// "media_id":MEDIA_ID,
// }
```
### 上传视频
```php
$result = $material->uploadVideo("/path/to/your/video.mp4", "视频标题", "视频描述"); // 请使用绝对路径写法!除非你正确的理解了相对路径(好多人是没理解对的)!
$mediaId = $result->media_id;
// {
// "media_id":MEDIA_ID,
// }
```
### 上传缩略图
用于视频封面或者音乐封面。
```php
$result = $material->uploadThumb("/path/to/your/thumb.jpg"); // 请使用绝对路径写法!除非你正确的理解了相对路径(好多人是没理解对的)!
$mediaId = $result->media_id;
// {
// "media_id":MEDIA_ID,
// }
```
### 上传永久图文消息
图文消息没有临时一说。
```php
use EasyWeChat\Message\Article;
// 上传单篇图文
$article = new Article([
'title' => 'xxx',
'thumb_media_id' => $mediaId,
//...
]);
$material->uploadArticle($article);
// 或者多篇图文
$material->uploadArticle([$article, $article2, ...]);
```
### 修改永久图文消息
有三个参数:
- `$mediaId` 要更新的文章的 `mediaId`
- `$article` 文章内容,`Article` 实例或者 全字段数组
- `$index` 要更新的文章在图文消息中的位置(多图文消息时,此字段才有意义,单图片忽略此参数),第一篇为 0
```php
$result = $material->updateArticle($mediaId, new Article(...));
$mediaId = $result->media_id;
// or
$result = $material->updateArticle($mediaId, [
'title' => 'xxx',
'thumb_media_id' => 'xxx',
// ...
]);
// 指定更新多图文中的第 2 篇
$result = $material->updateArticle($mediaId, new Article(...), 1); // 第 2 篇
```
### 上传永久文章内容图片
> 注意:微信图片上传服务有敏感检测系统,图片内容如果含有敏感内容,如色情,商品推广,虚假信息等,上传可能失败。
返回值中 url 就是上传图片的 URL可用于后续群发中放置到图文消息中。
```php
$result = $material->uploadArticleImage($path);
$url = $result->url;
//{
// "url": "http://mmbiz.qpic.cn/mmbiz/gLO17UPS6FS2xsypf378iaNhWacZ1G1UplZYWEYfwvuU6Ont96b1roYsCNFwaRrSaKTPCUdBK9DgEHicsKwWCBRQ/0"
//}
```
### 获取永久素材
```php
$resource = $material->get($mediaId);
```
如果请求的素材为图文消息,则响应如下:
```
{
"news_item": [
{
"title":TITLE,
"thumb_media_id"::THUMB_MEDIA_ID,
"show_cover_pic":SHOW_COVER_PIC(0/1),
"author":AUTHOR,
"digest":DIGEST,
"content":CONTENT,
"url":URL,
"content_source_url":CONTENT_SOURCE_URL
},
//多图文消息有多篇文章
]
}
```
如果返回的是视频消息素材,则内容如下:
```
{
"title":TITLE,
"description":DESCRIPTION,
"down_url":DOWN_URL,
}
```
其他类型的素材消息,则响应的直接为素材的内容,开发者可以自行保存为文件。例如
```
$image = $material->get($mediaId);
file_put_contents('/foo/abc.jpg', $image);
```
### 获取永久素材列表
参考:[微信公众平台开发者文档:获取永久素材列表](http://mp.weixin.qq.com/wiki/12/2108cd7aafff7f388f41f37efa710204.html)
- `$type` 素材的类型,图片(`image`)、视频(`video`)、语音 `voice`)、图文(`news`
- `$offset` 从全部素材的该偏移位置开始返回,可选,默认 `0`0 表示从第一个素材 返回
- `$count` 返回素材的数量,可选,默认 `20`, 取值在 1 到 20 之间
```php
$material->lists($type, $offset, $count);
```
example:
```
$lists = $material->lists('image', 0, 10);
```
图片、语音、视频 等类型的返回如下
```
{
"total_count": TOTAL_COUNT,
"item_count": ITEM_COUNT,
"item": [{
"media_id": MEDIA_ID,
"name": NAME,
"update_time": UPDATE_TIME,
"url":URL
},
//可能会有多个素材
]
}
```
永久图文消息素材列表的响应如下:
```
{
"total_count": TOTAL_COUNT,
"item_count": ITEM_COUNT,
"item": [{
"media_id": MEDIA_ID,
"content": {
"news_item": [{
"title": TITLE,
"thumb_media_id": THUMB_MEDIA_ID,
"show_cover_pic": SHOW_COVER_PIC(0 / 1),
"author": AUTHOR,
"digest": DIGEST,
"content": CONTENT,
"url": URL,
"content_source_url": CONTETN_SOURCE_URL
},
//多图文消息会在此处有多篇文章
]
},
"update_time": UPDATE_TIME
},
//可能有多个图文消息item结构
]
}
```
### 获取素材计数
```php
$stats = $material->stats();
// {
// "voice_count":COUNT,
// "video_count":COUNT,
// "image_count":COUNT,
// "news_count":COUNT
// }
```
### 删除永久素材;
```php
$material->delete($mediaId);
```
## 临时素材 API
上传的临时多媒体文件有格式和大小限制,如下:
- 图片image: 1M支持 `JPG` 格式
- 语音voice2M播放长度不超过 `60s`,支持 `AMR\MP3` 格式
- 视频video10MB支持 `MP4` 格式
- 缩略图thumb64KB支持 `JPG` 格式
### 上传图片
> 注意:微信图片上传服务有敏感检测系统,图片内容如果含有敏感内容,如色情,商品推广,虚假信息等,上传可能失败。
```php
$temporary->uploadImage($path);
```
### 上传声音
```php
$temporary->uploadVoice($path);
```
### 上传视频
```php
$temporary->uploadVideo($path, $title, $description);
```
### 上传缩略图
用于视频封面或者音乐封面。
```php
$temporary->uploadThumb($path);
```
### 获取临时素材内容
比如图片、视频、声音等二进制流内容。
```php
$content = $temporary->getStream($mediaId);
file_put_contents('/tmp/abc.jpg', $content);// 请使用绝对路径写法!除非你正确的理解了相对路径(好多人是没理解对的)!
```
### 下载临时素材到本地
其实就是上一个 API 的封装。
```php
$temporary->download($mediaId, "/tmp/", "abc.jpg");
```
参数说明:
- `$directory` 为目标目录,
- `$filename` 为新的文件名,可以为空,默认使用 `$mediaId` 作为文件名。
更多请参考 [微信官方文档](http://mp.weixin.qq.com/wiki) `素材管理` 章节

View File

@@ -0,0 +1,120 @@
# 自定义菜单
3.0 的菜单组件有所简化,相比 2.x 版本变化如下:
- 去除 `MenuItem` 类,创建菜单直接使用数组不再支持 `callback``MenuItem` 类似的繁杂的方式
- `set()` 方法与 `addConditional()` 合并为一个方法 `add()`
- `get()` 改名为 `all()`
- `delete()``deleteById()` 合并为 `destroy()`
- 所有 API 的返回值非调用失败情况均为官方文档原样返回Collection形式不再取返回值中部分 `key` 返回。
> 例如原来的 `get()` 方法,官方返回的数组为: `{ menu: [...]}`SDK 取了其中的 `menu` 内容作为返回值,在 3.0 后将直接整体返回。
## 获取菜单模块实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$menu = $app->menu;
```
## API 列表
### 读取(查询)已设置菜单
微信的菜单读取有两个不同的方式:
一种叫 **[查询菜单](http://mp.weixin.qq.com/wiki/5/f287d1a5b78a35a8884326312ac3e4ed.html)**,在 SDK 中以 `all()` 方法来调用:
```php
$menus = $menu->all();
```
另外一种叫 **[获取自定义菜单](http://mp.weixin.qq.com/wiki/14/293d0cb8de95e916d1216a33fcb81fd6.html)**,使用 `current()` 方法来调用:
```php
$menus = $menu->current();
```
### 添加菜单
#### 添加普通菜单
```php
$buttons = [
[
"type" => "click",
"name" => "今日歌曲",
"key" => "V1001_TODAY_MUSIC"
],
[
"name" => "菜单",
"sub_button" => [
[
"type" => "view",
"name" => "搜索",
"url" => "http://www.soso.com/"
],
[
"type" => "view",
"name" => "视频",
"url" => "http://v.qq.com/"
],
[
"type" => "click",
"name" => "赞一下我们",
"key" => "V1001_GOOD"
],
],
],
];
$menu->add($buttons);
```
以上将会创建一个普通菜单。
#### 添加个性化菜单
与创建普通菜单不同的是,需要在 `add()` 方法中将个性化匹配规则作为第二个参数传进去:
```php
$buttons = [
// ...
];
$matchRule = [
"tag_id" => "2",
"sex" => "1",
"country" => "中国",
"province" => "广东",
"city" => "广州",
"client_platform_type" => "2",
"language" => "zh_CN"
];
$menu->add($buttons, $matchRule);
```
### 删除菜单
有两种删除方式,一种是**全部删除**,另外一种是**根据菜单 ID 来删除**(删除个性化菜单时用ID 从查询接口获取)
```php
$menu->destroy(); // 全部
$menu->destroy($menuId);
```
### 测试个性化菜单
```php
$menus = $menu->test($userId);
```
> `$userId` 可以是粉丝的 OpenID也可以是粉丝的微信号。
返回 `$menus` 与指定的 `$userId` 匹配的菜单项。
更多关于微信自定义菜单 API 请参考: http://mp.weixin.qq.com/wiki `自定义菜单` 章节。

View File

@@ -0,0 +1,63 @@
# 企业支付
你在阅读本文之前确认你已经仔细阅读了:[微信支付 | 企业付款文档 ](https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1)。
## 配置
与其他支付接口一样,企业支付接口也需要配置如下参数,需要特别注意的是,企业支付相关的全部接口 **都需要使用 SSL 证书**,因此 **cert_path 以及 cert_key 必须正确配置**
```php
<?php
use EasyWeChat\Foundation\Application;
$options = [
'app_id' => 'your-app-id',
// payment
'payment' => [
'merchant_id' => 'your-mch-id',
'key' => 'key-for-signature',
'cert_path' => 'path/to/your/cert.pem',
'key_path' => 'path/to/your/key',
// ...
],
];
$app = new Application($options);
$merchantPay = $app->merchant_pay;
```
## 企业付款
企业付款使用的余额跟微信支付的收款并非同一账户,请注意充值。
### 发送接口
```php
<?php
$merchantPayData = [
'partner_trade_no' => str_random(16), //随机字符串作为订单号,跟红包和支付一个概念。
'openid' => $openid, //收款人的openid
'check_name' => 'NO_CHECK', //文档中有三种校验实名的方法 NO_CHECK OPTION_CHECK FORCE_CHECK
're_user_name'=>'张三', //OPTION_CHECK FORCE_CHECK 校验实名的时候必须提交
'amount' => 100, //单位为分
'desc' => '企业付款',
'spbill_create_ip' => '192.168.0.1', //发起交易的IP地址
];
$result = $merchantPay->send($merchantPayData);
```
> 更多参数请参考官方文档中参数列表。
## 查询付款信息
用于商户对已发放的企业支付进行查询企业支付的具体信息。
```php
$partnerTradeNo = "商户系统内部的订单号partner_trade_no";
$merchantPay->query($partnerTradeNo);
```

View File

@@ -0,0 +1,31 @@
# 多客服消息转发
多客服的消息转发绝对是超级的简单,转发的消息类型为 `transfer`
```php
// 转发收到的消息给客服
$server->setMessageHandler(function($message) {
return new \EasyWeChat\Message\Transfer();
});
$result = $server->serve();
echo $result;
```
当然,你也可以指定转发给某一个客服:
```php
$server->setMessageHandler(function($message) {
$transfer = new \EasyWeChat\Message\Transfer();
$transfer->account($account);// 或者 $transfer->to($account);
return $transfer;
});
```
更多请参考 [微信官方文档](http://mp.weixin.qq.com/wiki/) **多客服消息转发** 章节

View File

@@ -0,0 +1,339 @@
# 消息
我把微信的 API 里的所有“消息”都按类型抽象出来了,也就是说,你不用区分它是回复消息还是主动推送消息,免去了你去手动拼装微信那帮 SB 那么恶心的 XML 以及乱七八糟命名不统一的 JSON 了,我替你承受这份苦,不要问是谁,我是雷锋他弟弟,雷管。
在阅读以下内容时请忽略是**接收消息**还是**回复消息**,后面我会给你讲它们的区别。
## 消息类型
消息分为以下几种:`文本``图片``视频``声音``链接``坐标``图文``文章` 和一种特殊的 `原始消息`
另外还有一种特殊的消息类型:**素材消息**,用于群发或者客服时发送已有素材用。
> 注意:回复消息与客服消息里的图文类型为:**图文**,群发与素材中的图文为**文章**
所有的消息类都在 `EasyWeChat\Message` 这个命名空间下, 下面我们来分开讲解:
### 文本消息
属性列表:
```
- content 文本内容
```
```php
<?php
use EasyWeChat\Message\Text;
$text = new Text(['content' => '您好overtrue。']);
// or
$text = new Text();
$text->content = '您好overtrue。';
// or
$text = new Text();
$text->setAttribute('content', '您好overtrue。');
```
### 图片消息
属性列表:
```
- media_id 媒体资源 ID
```
```php
<?php
use EasyWeChat\Message\Image;
$text = new Image(['media_id' => $mediaId]);
// or
$text = new Image();
$text->media_id = $mediaId; // or $text->mediaId = $media;
// or
$text = new Image();
$text->setAttribute('media_id', $mediaId);
```
### 视频消息
属性列表:
```
- title 标题
- description 描述
- media_id 媒体资源 ID
- thumb_media_id 封面资源 ID
```
```php
<?php
use EasyWeChat\Message\Video;
$video = new Video([
'title' => $title,
'media_id' => $mediaId,
'description' => '...',
// ...
]);
// or
$video = new Video();
$video->media_id = $mediaId; // or $video->mediaId = $media;
$video->description = 'video description...'; // or $video->description = $description;
// ...
// or
$video = new Video();
$video->setAttribute('media_id', $mediaId);
// ...
```
### 声音消息
属性列表:
```
- media_id 媒体资源 ID
```
```php
<?php
use EasyWeChat\Message\Voice;
$voice = new Voice(['media_id' => $mediaId]);
// or
$voice = new Voice();
$voice->media_id = $mediaId; // or $voice->mediaId = $media;
// or
$voice = new Voice();
$voice->setAttribute('media_id', $mediaId);
```
### 链接消息
> 微信目前不支持回复链接消息
### 坐标消息
> 微信目前不支持回复坐标消息
### 图文消息
属性列表:
```
- title 标题
- description 描述
- image 图片链接
- url 链接 URL
```
```php
<?php
use EasyWeChat\Message\News;
$news = new News([
'title' => $title,
'description' => '...',
'url' => $url,
'image' => $image,
// ...
]);
// or
$news = new News();
$news->title = 'EasyWeChat';
$news->description = '微信 SDK ...';
// ...
```
### 文章消息
属性列表:
```
- title 标题
- author 作者
- content 具体内容
- thumb_media_id 图文消息的封面图片素材id必须是永久mediaID
- digest 图文消息的摘要,仅有单图文消息才有摘要,多图文此处为空
- source_url 来源 URL
- show_cover 是否显示封面0 为 false即不显示1 为 true即显示
```
```php
<?php
use EasyWeChat\Message\Article;
$article = new Article([
'title' => 'EasyWeChat',
'author' => 'overtrue',
'content' => 'EasyWeChat 是一个开源的微信 SDK它... ...',
// ...
]);
// or
$article = new Article();
$article->title = 'EasyWeChat';
$article->author = 'overtrue';
$article->content = '微信 SDK ...';
// ...
```
### 素材消息
素材消息用于群发与客服消息时使用。
属性就一个:`media_id`
在构造时有两个参数:
- `$type` 素材类型,目前只支持:`mpnews``mpvideo``voice``image` 等。
- `$mediaId` 素材 ID从接口查询或者上传后得到。
```php
use EasyWeChat\Message\Material;
$material = new Material('mpnews', $mediaId);
```
以上呢,是所有微信支持的基本消息类型。
> 需要注意的是,你不需要关心微信的消息字段叫啥,因为这里我们使用了更标准的命名,然后最终在中间做了转换,所以你不需要关注。
### 原始消息
原始消息是一种特殊的消息,它的场景是:**你不想使用其它消息类型,你想自己手动拼消息**。比如,回复消息时,你想自己拼 XML那么你就直接用它就可以了
```php
use EasyWeChat\Message\Raw;
$message = new Raw('<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[image]]></MsgType>
<Image>
<MediaId><![CDATA[media_id]]></MediaId>
</Image>
</xml>');
```
比如,你要用于客服消息(客服消息是JSON结构)
```php
use EasyWeChat\Message\Raw;
$message = new Raw('{
"touser":"OPENID",
"msgtype":"text",
"text":
{
"content":"Hello World"
}
}');
```
总之,就是直接写微信接口要求的格式内容就好,此类型消息在 SDK 中不存在转换行为,所以请注意不要写错格式。
## 在 SDK 中使用消息
### 在服务端回复消息
在 [服务端](server.html) 一节中,我们讲了回复消息的写法:
```php
// ... 前面部分省略
$app = new Application($options);
$server = $app->server;
$server->setMessageHandler(function ($message) {
return "您好!欢迎关注我!";
});
$server->serve()->send();
```
上面 `return` 了一句普通的文本内容,这里只是为了方便大家,实际上最后会有一个隐式转换为 `Text` 类型的动作。
如果你要回复其它类型的消息,就需要返回一个具体的实例了,比如回复一个图片类型的消息:
```php
use EasyWeChat\Message\Image;
// ...
$server->setMessageHandler(function ($message) {
return new Image(['media_id' => '........']);
});
// ...
```
#### 回复多图文消息
多图文消息其实就是单图文消息的一个数组而已了:
```php
use EasyWeChat\Message\News;
// ...
$server->setMessageHandler(function ($message) {
$news1 = new News(...);
$news2 = new News(...);
$news3 = new News(...);
$news4 = new News(...);
return [$news1, $news2, $news3, $news4];
});
// ...
```
### 作为客服消息发送
在客服消息里的使用也一样,都是直接传入消息实例即可:
```php
use EasyWeChat\Message\Text;
$message = new Text(['content' => 'Hello world!']);
$result = $app->staff->message($message)->to($openId)->send();
//...
```
#### 发送多图文消息
多图文消息其实就是单图文消息的一个数组而已了:
```php
$news1 = new News(...);
$news2 = new News(...);
$news3 = new News(...);
$news4 = new News(...);
$app->staff->message([$news1, $news2, $news3, $news4])->to($openId)->send();
```
### 群发消息
请参考:[群发消息](broadcast.html)
## 消息转发给客服系统
参见:[多客服消息转发](message-transfer.html)

View File

@@ -0,0 +1,60 @@
title: 小程序
---
## 实例化
```php
<?php
use EasyWeChat\Foundation\Application;
$options = [
// ...
'mini_program' => [
'app_id' => 'component-app-id',
'secret' => 'component-app-secret',
'token' => 'component-token',
'aes_key' => 'component-aes-key'
],
// ...
];
$app = new Application($options);
$miniProgram = $app->mini_program;
```
## 登录
### 通过 Code 换取 SessionKey
```php
// 3.2 版本
$miniProgram->user->getSessionKey($code);
// 3.3 版本
$miniProgram->sns->getSessionKey($code);
```
## 加密数据解密
```php
$miniProgram->encryptor->decryptData($sessionKey, $iv, $encryptData);
```
## 数据分析
### API
- `summaryTrend($from, $to)` 概况趋势限定查询1天数据`$from` 要与 `$to` 相同;
- `dailyVisitTrend($from, $to)` 访问日趋势限定查询1天数据`$from` 要与 `$to` 相同;
- `weeklyVisitTrend($from, $to)` 访问周趋势, `$from` 为周一日期, `$to` 为周日日期;
- `monthlyVisitTrend($from, $to)` 访问月趋势, `$from` 为月初日期, `$to` 为月末日期;
- `visitDistribution($from, $to)` 访问分布限定查询1天数据`$from` 要与 `$to` 相同;
- `dailyRetainInfo($from, $to)` 访问日留存限定查询1天数据`$from` 要与 `$to` 相同;
- `weeklyRetainInfo($from, $to)` 访问周留存, `$from` 为周一日期, `$to` 为周日日期;
- `montylyRetainInfo($from, $to)` 访问月留存, `$from` 为月初日期, `$to` 为月末日期;
- `visitPage($from, $to)` 访问页面限定查询1天数据`$from` 要与 `$to` 相同;
### 代码示例
```php
$miniProgram->stats->summaryTrend('20170313', '20170313');
```

View File

@@ -0,0 +1,4 @@
# 其它
### 其它

View File

@@ -0,0 +1,142 @@
# 模板消息
模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$notice = $app->notice;
```
### API
+ `boolean setIndustry($industryId1, $industryId2)` 修改账号所属行业;
+ `array getIndustry()` 返回所有支持的行业列表,用于做下拉选择行业可视化更新;
+ `string addTemplate($shortId)` 添加模板并获取模板ID
+ `collection send($message)` 发送模板消息, 返回消息ID
+ `array getPrivateTemplates()` 获取所有模板列表;
+ `array deletePrivateTemplate($templateId)` 删除指定ID的模板。
非链接调用方法:
```php
$messageId = $notice->send([
'touser' => 'user-openid',
'template_id' => 'template-id',
'url' => 'xxxxx',
'data' => [
//...
],
]);
```
链式调用方法:
设置模板IDtemplate / templateId / uses
设置接收者openId: to / receiver
设置详情链接url / link / linkTo
设置模板数据data / with
以上方法都支持 `withXXX``andXXX` 形式链式调用
```php
$messageId = $notice->to($userOpenId)->uses($templateId)->andUrl($url)->data($data)->send();
// 或者
$messageId = $notice->to($userOpenId)->url($url)->template($templateId)->andData($data)->send();
// 或者
$messageId = $notice->withTo($userOpenId)->withUrl($url)->withTemplate($templateId)->withData($data)->send();
// 或者
$messageId = $notice->to($userOpenId)->url($url)->withTemplateId($templateId)->send();
// ... ...
```
## 示例:
### 模板
```
{{ first.DATA }}
商品明细:
名称:{{ name.DATA }}
价格:{{ price.DATA }}
{{ remark.DATA }}
```
发送模板消息:
```php
$userId = 'OPENID';
$templateId = 'ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY';
$url = 'http://overtrue.me';
$data = array(
"first" => "恭喜你购买成功!",
"name" => "巧克力",
"price" => "39.8元",
"remark" => "欢迎再次购买!",
);
$result = $notice->uses($templateId)->withUrl($url)->andData($data)->andReceiver($userId)->send();
var_dump($result);
// {
// "errcode":0,
// "errmsg":"ok",
// "msgid":200228332
// }
```
结果:
![notice-demo](http://7u2jwa.com1.z0.glb.clouddn.com/QQ20160111-0@2x.png)
## 模板数据
为了方便大家开发,我们拓展支持以下格式的模板数据,其它格式的数据可能会导致接口调用失败:
- 所有数据项颜色一样的(这是方便的一种方式):
```php
$data = array(
"first" => "恭喜你购买成功!",
"keynote1" => "巧克力",
"keynote2" => "39.8元",
"keynote3" => "2014年9月16日",
"remark" => "欢迎再次购买!",
);
```
默认颜色为'#173177', 你可以通过 `defaultColor($color)` 来修改
- 独立设置每个模板项颜色的:
+ 简便型:
```php
$data = array(
"first" => array("恭喜你购买成功!", '#555555'),
"keynote1" => array("巧克力", "#336699"),
"keynote2" => array("39.8元", "#FF0000"),
"keynote3" => array("2014年9月16日", "#888888"),
"remark" => array("欢迎再次购买!", "#5599FF"),
);
```
+ 复杂型(也是微信官方唯一支持的方式,估计没有人想这么用):
```php
$data = array(
"first" => array("value" => "恭喜你购买成功!", "color" => '#555555'),
"keynote1" => array("value" => "巧克力", "color" => "#336699"),
"keynote2" => array("value" => "39.8元","color" => "#FF0000"),
"keynote3" => array("value" => "2014年9月16日", "color" => "#888888"),
"remark" => array("value" => "欢迎再次购买!", "color" => "#5599FF"),
);
```
关于模板消息的使用请参考 [微信官方文档](http://mp.weixin.qq.com/wiki/)

View File

@@ -0,0 +1,212 @@
# 网页授权
## 关于 OAuth2.0
OAuth是一个关于授权authorization的开放网络标准在全世界得到广泛应用目前的版本是2.0版。
```
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
OAuth 授权流程
```
> 摘自:[RFC 6749](https://datatracker.ietf.org/doc/rfc6749/?include_text=1)
步骤解释:
A用户打开客户端以后客户端要求用户给予授权。
B用户同意给予客户端授权。
C客户端使用上一步获得的授权向认证服务器申请令牌。
D认证服务器对客户端进行认证以后确认无误同意发放令牌。
E客户端使用令牌向资源服务器申请获取资源。
F资源服务器确认令牌无误同意向客户端开放资源。
关于 OAuth 协议我们就简单了解到这里,如果还有不熟悉的同学,请 [Google 相关资料](https://www.google.com.hk/?gws_rd=ssl#safe=strict&q=OAuth2)
## 微信 OAuth
在微信里的 OAuth 其实有两种:[公众平台网页授权获取用户信息](http://mp.weixin.qq.com/wiki/9/01f711493b5a02f24b04365ac5d8fd95.html)、[开放平台网页登录](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN)。
它们的区别有两处,授权地址不同,`scope` 不同。
- **公众平台网页授权获取用户信息**
**授权 URL**: `https://open.weixin.qq.com/connect/oauth2/authorize`
**Scopes**: `snsapi_base``snsapi_userinfo`
- **开放平台网页登录**
**授权 URL**: `https://open.weixin.qq.com/connect/qrconnect`
**Scopes**: `snsapi_login`
他们的逻辑都一样:
1. 用户尝试访问一个我们的业务页面,例如: `/user/profile`
2. 如果用户已经登录,则正常显示该页面
2. 系统检查当前访问的用户并未登录(从 session 或者其它方式检查),则跳转到**跳转到微信授权服务器**(上面的两种中一种**授权 URL**),并告知微信授权服务器我的**回调URLredirect_uri=callback.php)**,此时用户看到蓝色的授权确认页面(`scope``snsapi_base` 时不显示)
4. 用户点击确定完成授权,浏览器跳转到**回调URL**: `callback.php` 并带上 `code` `?code=CODE&state=STATE`
5.`callback.php` 中得到 `code` 后,通过 `code` 再次向微信服务器请求得到 **网页授权 access_token**`openid`
6. 你可以选择拿 `openid` 去请求 API 得到用户信息(可选)
7. 将用户信息写入 SESSION。
8. 跳转到第 3 步写入的 `target_url` 页面(`/user/profile`)。
> 看懵了?没事,使用 SDK你不用管这么多。:smile:
>
> 注意上面的第3步redirect_uri=callback.php实际上我们会在 `callback.php` 后面还会带上授权目标页面 `user/profile`,所以完整的 `redirect_uri` 应该是下面的这样的PHP去拼出来`'redirect_uri='.urlencode('callback.php?target=user/profile')`
> 结果redirect_uri=callback.php%3Ftarget%3Duser%2Fprofile
## 逻辑组成
从上面我们所描述的授权流程来看我们至少有3个页面
1. **业务页面**,也就是需要授权才能访问的页面。
2. **发起授权页**,此页面其实可以省略,可以做成一个中间件,全局检查未登录就发起授权。
3. **授权回调页**接收用户授权后的状态并获取用户信息写入用户会话状态SESSION
## 开始之前
在开始之前请一定要记住,先登录公众号后台,找到**边栏 “开发”** 模块下的 **“接口权限”**,点击 **“网页授权获取用户基本信息”** 后面的修改,添加你的网页授权域名。
> 如果你的授权地址为:`http://www.abc.com/xxxxx`,那么请填写 `www.abc.com`,也就是说请填写与网址匹配的域名,前者如果填写 `abc.com` 是通过不了的。
## SDK 中 OAuth 模块的 API
在 SDK 中,我们使用名称为 `oauth` 的模块来完成授权服务,我们主要用到以下两个 API
### 发起授权
```php
$response = $app->oauth->scopes(['snsapi_userinfo'])
->redirect();
```
当你的应用是分布式架构且没有会话保持的情况下,你需要自行设置请求对象以实现会话共享。比如在 [Laravel](http://laravel.com) 框架中支持Session储存在Redis中那么需要这样
```php
$response = $app->oauth->scopes(['snsapi_userinfo'])
->setRequest($request)
->redirect();
//回调后获取user时也要设置$request对象
//$user = $app->oauth->setRequest($request)->user();
```
它的返回值 `$response` 是一个 [Symfony\Component\HttpFoundation\RedirectResponse](http://api.symfony.com/3.0/Symfony/Component/HttpFoundation/RedirectResponse.html) 实例。
你可以选择在框架中做一些正确的响应,比如在 [Laravel](http://laravel.com) 框架中控制器方法是要求返回响应值的,那么你就直接:
```php
return $response;
```
在有的框架 (比如yii2) 中是直接 `echo` 或者 `$this->display()` 这种的时候,你就直接:
```php
$response->send(); // Laravel 里请使用return $response;
```
### 获取已授权用户
```php
$user = $app->oauth->user();
// $user 可以用的方法:
// $user->getId(); // 对应微信的 OPENID
// $user->getNickname(); // 对应微信的 nickname
// $user->getName(); // 对应微信的 nickname
// $user->getAvatar(); // 头像网址
// $user->getOriginal(); // 原始API返回的结果
// $user->getToken(); // access_token 比如用于地址共享时使用
```
返回的 `$user` 是 [Overtrue\Socialite\User](https://github.com/overtrue/socialite/blob/master/src/User.php) 对象,你可以从该对象拿到[更多的信息](https://github.com/overtrue/socialite#user-interface)。
> :pray: 注意:`$user` 里没有 `openid` `$user->id` 便是 `openid`.
> 如果你想拿微信返回给你的原样的全部信息,请使用:$user->getOriginal();
`scope``snsapi_base``$oauth->user();` 对象里只有 `id`,没有其它信息。
## 网页授权实例
我们这里来用原生 PHP 写法举个例子,`oauth_callback` 是我们的授权回调URL (未urlencode编码的URL), `user/profile` 是我们需要授权才能访问的页面,它的 PHP 代码如下:
```php
// http://easywechat.org/user/profile
<?php
use EasyWeChat\Foundation\Application;
$config = [
// ...
'oauth' => [
'scopes' => ['snsapi_userinfo'],
'callback' => '/oauth_callback',
],
// ..
];
$app = new Application($config);
$oauth = $app->oauth;
// 未登录
if (empty($_SESSION['wechat_user'])) {
$_SESSION['target_url'] = 'user/profile';
return $oauth->redirect();
// 这里不一定是return如果你的框架action不是返回内容的话你就得使用
// $oauth->redirect()->send();
}
// 已经登录过
$user = $_SESSION['wechat_user'];
// ...
```
授权回调页:
```php
// http://easywechat.org/oauth_callback
<?php
use EasyWeChat\Foundation\Application;
$config = [
// ...
];
$app = new Application($config);
$oauth = $app->oauth;
// 获取 OAuth 授权结果用户信息
$user = $oauth->user();
$_SESSION['wechat_user'] = $user->toArray();
$targetUrl = empty($_SESSION['target_url']) ? '/' : $_SESSION['target_url'];
header('location:'. $targetUrl); // 跳转到 user/profile
```
上面的例子呢都是基于 `$_SESSION` 来保持会话的,在微信客户端中,你可以结合 COOKIE 来存储,但是有效期平台不一样时间也不一样,好像 Android 的失效会快一些,不过基本也够用了。
更多关于微信网页授权 API 请参考: http://mp.weixin.qq.com/wiki/
更多开放平台网页登录请参考https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

View File

@@ -0,0 +1,152 @@
# 微信开放平台
### 实例化
```php
<?php
use EasyWeChat\Foundation\Application;
$options = [
// ...
'open_platform' => [
'app_id' => 'component-app-id',
'secret' => 'component-app-secret',
'token' => 'component-token',
'aes_key' => 'component-aes-key'
],
// ...
];
$app = new Application($options);
$openPlatform = $app->open_platform;
```
### 监听微信服务器推送事件
公众号第三方平台推送的有四个事件:授权成功(`authorized`),授权更新(`updateauthorized`),授权取消(`unauthorized`),以及 `component_verify_ticket`
本 SDK 默认处理方式为:
- `authorized` / `updateauthorized`: 获取授权方(Authorizer)的所有信息,并缓存 `authorizer_access_token``authorizer_refresh_token`,授权方的信息则需要开发者手动处理。
- `unauthorized`: 删除 `authorizer_access_token``authorizer_refresh_token` 的缓存。
- `component_verify_ticket`: 缓存 `component_veirfy_ticket`
当然也允许自定义处理这些事件,不过以上默认处理仍然会先执行,为的是帮助开发者免去缓存的困扰。
```php
// 默认处理方式
$openPlatform->server->serve();
// 自定义处理
$openPlatform->server->setMessageHandler(function($event) {
// 事件类型常量定义在 \EasyWeChat\OpenPlatform\Guard 类里
switch ($event->InfoType) {
case 'authorized':
// ...
case 'unauthorized':
// ...
case 'updateauthorized':
// ...
case 'component_verify_ticket':
// ...
}
});
$openPlatform->server->serve();
// 或者
$openPlatform->server->listen(function ($event) {
switch ($event->InfoType) {
// ...
}
});
```
#### 授权成功,授权更新
这两个事件下SDK 默认抓取了所有授权方所有的信息,并缓存 `authorizer_access_token``authorizer_refresh_token`,授权方的信息为原微信 API 的返回结果,由开发者自行处理,比如保存到数据库。
```php
// 自定义处理
// 其中 $event 变量里有微信推送事件本身的信息,也有授权方所有的信息。
$openPlatform->server->setMessageHandler(function($event) {
// 事件类型常量定义在 \EasyWeChat\OpenPlatform\Guard 类里
switch ($event->InfoType) {
case 'authorized':
// 授权信息,主要是 token 和授权域
$info1 = $event->authorization_info;
// 授权方信息,就是授权方公众号的信息了
$info2 = $event->authorizer_info;
}
});
```
目前 SDK 对这两个事件的处理方式没有区别。
#### 授权取消
SDK 默认处理:删除 `authorizer_access_token``authorizer_refresh_token` 的缓存。开发者可以自行处理数据库删除授权方信息等操作。
#### 推送 component_verify_ticket
在公众号第三方平台创建审核通过后微信服务器会向其“授权事件接收URL”每隔10分钟定时推送 `component_verify_ticket`。SDK 内部已实现缓存 `component_veirfy_ticket`,无需开发者另行缓存。
需要在URL路由中写上触发代码并且注册路由后需要等待微信服务器推送 `component_verify_ticket`,才能进行后续操作,否则报"Component verify ticket does not exists."
### 调用 API
#### 设置授权方的 App Id
开发者必须设置授权方来调用 API。
```php
$openPlatform = new Application($options)->open_platform;
// 加载授权方信息,比如 $authorizer = Authorizer::find($id);
$authorizerAppId = $authorizer->app_id;
$authorizerRefreshToken = $authorizer->refresh_token;
$app = $openPlatform->createAuthorizerApplication($authorizerAppId, $authorizerRefreshToken);
// 然后调用方法和普通调用一致。
// ...
```
### 授权 API
#### 获取预授权网址
```php
// 直接跳转
$response = $openPlatform->pre_auth->redirect('https://domain.com/callback');
// 获取跳转的链接
$response->getTargetUrl();
```
用户授权后会带上 `code` 跳转到 `redirect` 指定的链接。
#### 使用授权码换取公众号的接口调用凭据和授权信息
```php
// 使用授权码换取公众号的接口调用凭据和授权信息
// Optional: $authorizationCode 不传值时会自动获取 URL 中 auth_code 值
$openPlatform->getAuthorizationInfo($authorizationCode = null);
```
#### 获取授权方的公众号帐号基本信息
```php
$openPlatform->getAuthorizerInfo($authorizerAppId);
```
#### 获取授权方的选项设置信息
```php
$openPlatform->getAuthorizerOption($authorizerAppId, $optionName);
```
#### 设置授权方的选项信息
```php
$openPlatform->setAuthorizerOption($authorizerAppId, $optionName, $optionValue);
```

View File

@@ -0,0 +1,64 @@
# EasyWeChat
## EasyWeChat 是什么?
EasyWeChat 是一个开源的 [微信](http://www.wechat.com) 非官方 SDK。
EasyWeChat 的安装非常简单,因为它是一个标准的 [Composer](https://getcomposer.org/) 包,这意味着任何满足下列安装条件的 PHP 项目支持 Composer 都可以使用它。
### 环境需求
- PHP >= 5.5.9 (其实你不必惊讶PHP 7 的时代了)
- [PHP cURL 扩展](http://php.net/manual/en/book.curl.php)
- [PHP OpenSSL 扩展](http://php.net/manual/en/book.openssl.php)
- [PHP fileinfo 拓展](http://php.net/manual/en/book.fileinfo.php) 素材管理模块需要用到
### 加入我们
<a target="_blank" href="http://shang.qq.com/wpa/qunwpa?idkey=b4dcf3ec51a7e8c3c3a746cf450ce59895e5c4ec4fbcb0f80c2cd97c3c6e63e9"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="EasyWeChat SDK 交流群" title="EasyWeChat SDK 交流群"></a> ID: 319502940
> 为了避免广告及不看文档用户,加群需要付费,所以请使用 能支持群费的客户端。
> 另外:付费加群不代表我们有责任在群里回答你的问题,所以请认真阅读微信官方文档与 SDK 使用文档再使用,否则提的低级问题不会有人理你
> 不喜勿加,谢谢!
> 除非你发现了明确的 Bug否则不要在群里 @ 我,否则直接 T 人(当然了,不退群费):pray:
你有以下两种方式加入到我们中来,为广大开发者提供更优质的免费开源的服务:
- **贡献代码**:我们 3.0 的代码都在 [overtrue/wechat](https://github.com/overtrue/wechat) ,你可以提交 PR 到任何一个项目,当然,前提是代码质量必须是 OK 的。
- **翻译或补充文档**:我们的文档在:[w7corp/EasyWeChat/docs](https://github.com/w7corp/easywechat/tree/master/docs),你可以选择补充文档或者参与英文文档的翻译,目前有 `zh-cn``en` 两个分支,你可以提交对应的 PR 到目标分支参与翻译工作。
### 开始之前
本 SDK 不是一个全新再造的东西,所以我不会从 0 开始教会你开发微信,你完全有必要在使用本 SDK 前做好以下工作:
- 具备 PHP 基础知识,不要连闭包是啥都不明白,可以参考我在知乎的回答: [想要开发自己的 PHP 框架需要那些知识储备?](http://www.zhihu.com/question/26635323/answer/33812516)
- 熟悉 PHP 常见的知识自动加载、composer 的使用、JSON 处理、Curl 的使用等;
- **仔细阅读并看懂** (不是**看过**,是**看明白+看完** :exclamation: [微信官方文档](http://mp.weixin.qq.com/wiki/13/80a1a25adbc46faf2716774c423b3151.html) [微信开放平台文档](https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318292&token=&lang=zh_CN)
- 明白微信接口的组成,自有服务器、微信服务器、公众号(还有其它各种号)、测试号、以及通信原理(交互过程);
- 了解基本的 HTTP 协议Header 头、请求方式GET\POST\PUT\PATCH\DELETE
- 基本的 Debug 技能,查看 php 日志nginx 日志等。
如果你不具备这些知识,请不要使用,因为用起来会比较痛苦。
另外你有必要看一下以下的链接:
- https://phphub.org/topics/535
- http://laravel-china.github.io/php-the-right-way/
如果你在群里问以下类似的问题,这真的是你没有做好上面的工作:
- "为啥我的不行啊,请问服务器日志怎么看啊?"
- "请问这是什么原因啊?[结果/报错截图]"
- "请问这个 SDK 怎么用啊?"
- "谁能告诉我这个 SDK 是怎么安装的啊?"
- "怎么接收用户发的消息啊?"
- "为啥我的报这个错啊Class XXXX not found..."
- ...
我们专门针对一些容易出现的通用问题已经做了汇总: [疑难解答](troubleshooting) ,如果你在问题疑难解答没找到你出现的问题,那么可以在这里提问 [GitHub](https://github.com/overtrue/wechat/issues),提问请描述清楚你用的版本,你的做法是什么,不然别人没法帮你。
最后,请 **不要在 QQ 单独找我提问**,除非你是发现了明显的 bug。有问题先审查代码看文档, 再 google然后 去群里发个问题,带上你的代码,重现流程,大家有空的会帮忙你解答。谢谢合作!:pray:
### 打赏支持
这是一个开源的项目,我们没有收费服务,你如果觉得你从中获益,简化了你的开发工作,你可以 [打赏](https://github.com/sponsors/overtrue) 来支持我们。

View File

@@ -0,0 +1,422 @@
# 支付
你在阅读本文之前确认你已经仔细阅读了:[微信支付 | 商户平台开发文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)。
网友贡献的教程:[小能手马闯set 发布在 Laravel-China 的文章《基于 Laravel5.1 LTS 版的微信开发》](https://laravel-china.org/topics/3146)
## 配置
配置在前面的例子中已经提到过了,支付的相关配置如下:
```php
<?php
use EasyWeChat\Foundation\Application;
$options = [
// 前面的appid什么的也得保留哦
'app_id' => 'xxxx',
// ...
// payment
'payment' => [
'merchant_id' => 'your-mch-id',
'key' => 'key-for-signature',
'cert_path' => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
'key_path' => 'path/to/your/key', // XXX: 绝对路径!!!!
'notify_url' => '默认的订单回调地址', // 你也可以在下单时单独设置来想覆盖它
// 'device_info' => '013467007045764',
// 'sub_app_id' => '',
// 'sub_merchant_id' => '',
// ...
],
];
$app = new Application($options);
$payment = $app->payment;
```
## 创建订单
### 正常模式
```php
<?php
use EasyWeChat\Payment\Order;
$attributes = [
'trade_type' => 'JSAPI', // JSAPINATIVEAPP...
'body' => 'iPad mini 16G 白色',
'detail' => 'iPad mini 16G 白色',
'out_trade_no' => '1217752501201407033233368018',
   'total_fee'       => 5388, // 单位:分
   'notify_url'       => 'http://xxx.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
'openid' => '当前用户的 openid', // trade_type=JSAPI此参数必传用户在商户appid下的唯一标识
// ...
];
$order = new Order($attributes);
```
### 子服务商模式
```php
<?php
use EasyWeChat\Payment\Order;
$attributes = [
'trade_type' => 'JSAPI', // JSAPINATIVEAPP...
'body' => 'iPad mini 16G 白色',
'detail' => 'iPad mini 16G 白色',
'out_trade_no' => '1217752501201407033233368018',
'total_fee' => 5388, // 单位:分
'notify_url' => 'http://xxx.com/order-notify', // 支付结果通知网址,如果不设置则会使用配置里的默认地址
   'sub_openid'        => '当前用户的 openid', // 如果传入sub_openid, 请在实例化Application时, 同时传入$sub_app_id, $sub_merchant_id
   // ...
];
$order = new Order($attributes);
```
通知url必须为直接可访问的url不能携带参数。示例notify_url“https://pay.weixin.qq.com/wxpay/pay.action”
## 下单接口
### 刷卡支付
[官方文档](https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10)
```php
$result = $payment->pay($order);
```
> 也许你需要生成二维码图片,参考页面底部相关的包推荐
## 统一下单
[公众号支付](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1)、[扫码支付](https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1)、[APP 支付](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1) 都统一使用此接口完成订单的创建。
```php
$result = $payment->prepare($order);
if ($result->return_code == 'SUCCESS' && $result->result_code == 'SUCCESS'){
$prepayId = $result->prepay_id;
}
```
## 支付结果通知
在用户成功支付后,微信服务器会向该 **订单中设置的回调URL** 发起一个 POST 请求,请求的内容为一个 XML。里面包含了所有的详细信息具体请参考
[支付结果通用通知](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7)
在本 SDK 中处理回调真的再简单不过了请求验证你就不用管了SDK 已经为你做好了,你只需要关注业务即可:
```php
$response = $app->payment->handleNotify(function($notify, $successful){
// 你的逻辑
return true; // 或者错误消息
});
$response->send(); // Laravel 里请使用return $response;
```
这里需要注意的有几个点:
1. `handleNotify` 只接收一个 [`callable`](http://php.net/manual/zh/language.types.callable.php) 参数,通常用一个匿名函数即可。
2. 该匿名函数接收两个参数,这两个参数分别为:
- `$notify` 为封装了通知信息的 `EasyWeChat\Support\Collection` 对象,前面已经讲过这里就不赘述了,你可以以对象或者数组形式来读取通知内容,比如:`$notify->total_fee` 或者 `$notify['total_fee']`
- `$successful` 这个参数其实就是判断 **用户是否付款成功了**result_code == 'SUCCESS'
3. 该函数返回值就是告诉微信 **“我是否处理完成”**,如果你返回一个 `false` 或者一个具体的错误消息,那么微信会在稍后再次继续通知你,直到你明确的告诉它:“我已经处理完成了”,在函数里 `return true;` 代表处理完成。
4. `handleNotify` 返回值 `$response` 是一个 Response 对象,如果你要直接输出,使用 `$response->send()`, 在一些框架里不是输出而是返回:`return $response`
通常我们的处理逻辑大概是下面这样(**以下只是伪代码**
```php
$response = $app->payment->handleNotify(function($notify, $successful){
// 使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$order = 查询订单($notify->out_trade_no);
if (!$order) { // 如果订单不存在
return 'Order not exist.'; // 告诉微信,我已经处理完了,订单没找到,别再通知我了
}
// 如果订单存在
// 检查订单是否已经更新过支付状态
if ($order->paid_at) { // 假设订单字段“支付时间”不为空代表已经支付
return true; // 已经支付成功了就不再更新了
}
// 用户是否支付成功
if ($successful) {
// 不是已经支付状态则修改为已经支付状态
$order->paid_at = time(); // 更新支付时间为当前时间
$order->status = 'paid';
} else { // 用户支付失败
$order->status = 'paid_fail';
}
$order->save(); // 保存订单
return true; // 返回处理完成
});
return $response;
```
> 注意:请把 “支付成功与否” 与 “是否处理完成” 分开,它俩没有必然关系。
> 比如:微信通知你用户支付完成,但是支付失败了(result_code 为 'FAIL'),你应该**更新你的订单为支付失败**,但是要**告诉微信处理完成**。
## 撤销订单API
目前只有 **刷卡支付** 有此功能。
> 调用支付接口后请勿立即调用撤销订单API建议支付后至少15s后再调用撤销订单接口。
```php
$orderNo = "商户系统内部的订单号out_trade_no";
$payment->reverse($orderNo);
```
或者:
```php
$orderNo = "微信的订单号transaction_id";
$payment->reverseByTransactionId($orderNo);
```
## 查询订单
该接口提供所有微信支付订单的查询,商户可以通过该接口主动查询订单状态,完成下一步的业务逻辑。
需要调用查询接口的情况:
- 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
- 调用支付接口后,返回系统错误或未知交易状态情况;
- 调用被扫支付API返回USERPAYING的状态
- 调用关单或撤销接口API之前需确认支付状态
```php
$orderNo = "商户系统内部的订单号out_trade_no";
$payment->query($orderNo);
```
或者:
```php
$orderNo = "微信的订单号transaction_id";
$payment->queryByTransactionId($orderNo);
```
## 关闭订单
> 注意订单生成后不能马上调用关单接口最短调用时间间隔为5分钟。
```php
$orderNo = "商户系统内部的订单号out_trade_no";
$payment->close($orderNo);
```
## 申请退款
当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
注意:
> 1、交易时间超过一年的订单无法提交退款
> 2、微信支付退款支持单笔交易分多次退款多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
```php
$payment->refund(订单号,退款单号,总金额,退款金额,操作员,退款单号类型(out_trade_no/transaction_id),退款账户(REFUND_SOURCE_UNSETTLED_FUNDS/REFUND_SOURCE_RECHARGE_FUNDS))
```
参考https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
例子:
```php
# 1. 使用商户订单号退款
$result = $payment->refund($orderNo, $refundNo, 100); // 总金额 100 退款 100操作员商户号
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80); // 总金额 100 退款 80操作员商户号
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109); // 总金额 100 退款 80操作员1900000109
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no'); // 总金额 100 退款 80操作员1900000109, 退款单号:使用商户订单号退款
// or
$result = $payment->refund($orderNo, $refundNo, 100, 80, 1900000109, 'out_trade_no', 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100 退款 80操作员1900000109, 退款单号:使用商户订单号退款, 退款账户:可用余额退款
# 2. 使用 TransactionId 退款
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100); // 总金额 100 退款 100操作员商户号
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80); // 总金额 100 退款 80操作员商户号
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109); // 总金额 100 退款 80操作员1900000109
// or
$result = $payment->refundByTransactionId($transactionId, $refundNo, 100, 80, 1900000109, 'REFUND_SOURCE_RECHARGE_FUNDS'); // 总金额 100 退款 80操作员1900000109退款账户可用余额退款
```
> $refundNo 为商户退款单号,自己生成用于自己识别即可。
## 查询退款
提交退款申请后通过调用该接口查询退款状态。退款有一定延时用零钱支付的退款20分钟内到账银行卡支付的退款3个工作日后重新查询退款状态。
```php
$result = $payment->queryRefund($outTradeNo);
// or
$result = $payment->queryRefundByTransactionId($transactionId);
// or
$result = $payment->queryRefundByRefundNo($outRefundNo);
// or
$result = $payment->queryRefundByRefundId($refundId);
```
## 下载对账单
```php
$bill = $payment->downloadBill('20140603')->getContents(); // type: ALL
// or
$bill = $payment->downloadBill('20140603', 'SUCCESS')->getContents(); // type: SUCCESS
// bill 为 csv 格式的内容
// 保存为文件
file_put_contents('YOUR/PATH/TO/bill-20140603.csv', $bill);
```
第二个参数为类型:
- **ALL**:返回当日所有订单信息(默认值)
- **SUCCESS**:返回当日成功支付的订单
- **REFUND**:返回当日退款订单
- **REVOKED**:已撤销的订单
## 测速上报
```php
$payment->report($api, $timeConsuming, $resultCode, $returnCode);
// or
$payment->report($api, $timeConsuming, $resultCode, $returnCode, [
'err_code' => 'xxxx',
'err_code_des' => '...',
'out_trade_no' => '...',
'user_ip' => '...',
]);
```
## 转换短链接
```php
$shortUrl = $payment->urlShorten('http://easywechat.org');
```
## 授权码查询OPENID接口
```php
$response = $payment->authCodeToOpenId($authCode);
$response->openid;
```
## 生成支付 JS 配置
有两种发起支付的方式:[WeixinJSBridge](https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6), [JSSDK](https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN)
1. WeixinJSBridge:
```php
$json = $payment->configForPayment($prepayId); // 返回 json 字符串,如果想返回数组,传第二个参数 false
```
javascript:
```js
...
WeixinJSBridge.invoke(
'getBrandWCPayRequest', <?= $json ?>,
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
// 使用以上方式判断前端返回,微信团队郑重提示:
// res.err_msg将在用户支付成功后返回
// ok但并不保证它绝对可靠。
}
}
);
...
```
2. JSSDK:
```php
$config = $payment->configForJSSDKPayment($prepayId); // 返回数组
```
javascript:
```js
wx.chooseWXPay({
timestamp: <?= $config['timestamp'] ?>,
nonceStr: '<?= $config['nonceStr'] ?>',
package: '<?= $config['package'] ?>',
signType: '<?= $config['signType'] ?>',
paySign: '<?= $config['paySign'] ?>', // 支付签名
success: function (res) {
// 支付成功后的回调函数
}
});
```
## 生成共享收货地址 JS 配置
1. 发起 OAuth 授权:
```php
use EasyWeChat\Support\Url as UrlHelper;
// 检查当前不是微信 oauth 的回调,则跳过去授权
// 注意,授权回调地址为当前页
if (empty($_GET['code'])) {
$currentUrl = UrlHelper::current(); // 获取当前页 URL
$response = $app->oauth->scopes(['snsapi_base'])->redirect($currentUrl);
return $response; // or echo $response;
}
// 授权回来
$oauthUser = $app->oauth->user();
$token = $oauthUser->getAccessToken();
$configForPickAddress = $payment->configForShareAddress($token);
// 拿着这个生成好的配置 $configForPickAddress 去订单页(或者直接显示订单页)写 js 调用了
// ...
```
## 生成 APP 支付配置
```php
$config = $payment->configForAppPayment($prepayId);
```
`$config` 为数组格式,你可以用 API 返回给客户端
# 二维码生成工具推荐
你也许需要生成二维码,那么以下这些供参考:
- https://github.com/endroid/QrCode
- https://github.com/Bacon/BaconQrCode
- https://github.com/SimpleSoftwareIO/simple-qrcode (Bacon/BaconQrCode 的 Laravel 版本)
- https://github.com/aferrandini/PHPQRCode

View File

@@ -0,0 +1,154 @@
# 门店
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$poi = $app->poi;
```
## 创建门店
用 POI 接口新建门店时所使用的图片 url 必须为微信自己域名的 url,因此需要先用上传图片接 口上传图片并获取 url,再创建门店。上传的图片限制文件大小限制 1MB,支持 JPG 格式,图片接口请参考:[TODO](/)
```php
$poi->create($baseInfo);
```
- `$baseInfo` 为门店的基本信息数组
example:
```php
<?php
$info = array(
"sid" => "33788392",
"business_name" => "麦当劳",
"branch_name" => "艺苑路店",
"province" => "广东省",
"city" => "广州市",
"district" => "海珠区",
"address" => "艺苑路 11 号",
"telephone" => "020-12345678",
"categories" => array("美食,快餐小吃"),
"offset_type" => 1,
"longitude" => 115.32375,
"latitude" => 25.097486,
"photo_list" => array(
array("photo_url" => "https://XXX.com"),
array("photo_url" => "https://XXX.com"),
),
"recommend" => "麦辣鸡腿堡套餐,麦乐鸡,全家桶",
"special" => "免费 wifi,外卖服务",
"introduction" => "麦当劳是全球大型跨国连锁餐厅,1940 年创立于美国,在世界上大约拥有 3 万间分店。主要售卖汉堡包,以及薯条、炸鸡、汽水、冰品、沙拉、水果等 快餐食品",
"open_time" => "8:00-20:00",
"avg_price" => 35,
);
$result = $poi->create($info); // true or exception
```
> 注意:新创建的门店在审核通过后,会以事件形式推送给商户填写的回调URL
## 获取指定门店信息
```php
$poi->get($poiId);
```
- `$poiId` 为门店ID
example:
```php
$info = $poi->get(271262077);
var_dump($info->business_name); // 麦当劳
var_dump($info->introduction); // 麦当劳是全球大型跨国连锁餐厅...
var_dump($info->toArray());// array('business_name' => '麦当劳', 'branch_name' => '艺苑路店', ...);
```
## 获取门店列表
```php
$poi->lists($begin, $limit);// begin:0, limit:10
```
- `$begin` 就是查询起点,`MySQL` 里的 `offset`
- `$limit` 查询条数,同 `MySQL` 里的 `limit`
> 两参数均可选
example:
```php
$pois = $poi->lists(0, 2);// 取2条记录
//
//[
// {
// "sid": "100",
// "poi_id": "271864249",
// "business_name": "麦当劳",
// "branch_name": "艺苑路店",
// "address": "艺苑路 11 号",
// "available_state": 3
// },
// {
// "sid": "101",
// "business_name": "麦当劳",
// "branch_name": "赤岗路店",
// "address": "赤岗路 102 号",
// "available_state": 4
// }
//]
```
## 修改门店信息
商户可以通过该接口,修改门店的服务信息,包括:图片列表、营业时间、推荐、特色服务、简 介、人均价格、电话 7 个字段。目前基础字段包括(名称、坐标、地址等不可修改)。
```php
$poi->update($poiId, $data);
```
- `$poiId` 为门店ID
- `$data` 需要更新的部分数据,**若有填写内容则为覆盖更新,若无内容则视为不 修改,维持原有内容。photo_list 字段为全列表覆盖,若需要增加图片,需将之前图片同样放入 list 中,在其后增加新增图片。如:已有 A、B、C 三张图片,又要增加 D、E 两张图,则需要调 用该接口,photo_list 传入 A、B、C、D、E 五张图片的链接。**
example:
```php
$data = array(
"telephone" => "020-12345678",
"recommend" => "麦辣鸡腿堡套餐,麦乐鸡,全家桶",
//...
);
$res = $poi->update(271262077, $data); //true or exception
```
## 删除门店
```php
$poi->delete($poiId);
```
example:
```php
$poi->delete(271262077);// true or exception
```
## 错误码
- `invalid categories` 分类不合法,必须严格按照附表的分类填写
- `invalid photo url` 图片 url 不合法,必须使用接口 1 的图片上传 接口所获取的 url
- `poi audit state must be approved` 门店状态必须未审核通过
- `invalid poiid` poi_id 不正确
- `invalid args` 参数不正确,请检查 json 字段
- `system error` 系统错误,请稍后重试

View File

@@ -0,0 +1,69 @@
# 二维码
目前有2种类型的二维码
1. 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的**30天**后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
2. 永久二维码是无过期时间的但数量较少目前为最多10万个。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$qrcode = $app->qrcode;
```
## API
+ `Bag temporary($sceneId, $expireSeconds = null)` 创建临时二维码;
+ `Bag forever($sceneValue)` 创建永久二维码
+ `Bag card(array $card)` 创建卡券二维码
+ `string url($ticket)` 获取二维码网址,用法: `<img src="<?php $qrcode->url($qrTicket); ?>">`
### 创建临时二维码
```php
$result = $qrcode->temporary(56, 6 * 24 * 3600);
$ticket = $result->ticket;// 或者 $result['ticket']
$expireSeconds = $result->expire_seconds; // 有效秒数
$url = $result->url; // 二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片
```
### 创建永久二维码
```php
$result = $qrcode->forever(56);// 或者 $qrcode->forever("foo");
$ticket = $result->ticket; // 或者 $result['ticket']
$url = $result->url;
```
### 获取二维码网址
```php
$url = $qrcode->url($ticket);
```
### 创建卡券二维码
```php
$qrcode->card($card);
```
### 获取二维码内容
```php
$url = $qrcode->url($ticket);
$content = file_get_contents($url); // 得到二进制图片内容
file_put_contents(__DIR__ . '/code.jpg', $content); // 写入文件
```

View File

@@ -0,0 +1,11 @@
# 升级日志
## 3.0
- 新的架构
- 重写代码
- 更低的耦合
- 更规范的代码
- 更友好的调试支持
- 更完善的文档

View File

@@ -0,0 +1,21 @@
# 自动回复
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$reply = $app->reply;
```
## 获取当前设置的回复规则
```php
$reply->current();
```

View File

@@ -0,0 +1,32 @@
# 路线图
## 3.1
- 微信小店
- 新的卡券
- 设备管理
## 3.0
- 全新的架构,更清晰的模块拆分
- Debug 优化,便于快速定位问题
- 统一返回微信API原值以 Collection 类装载,便于便捷操作
- 大量模块重写
- 全新的支付模块
- 多类型缓存系统
- 新的命名空间EasyWeChat
- 专属网站与更清晰与文档http://easywechat.org
## 2.1
- 新增支付与红包
## 2.0
- 仅问题修复,不再增加新功能;
## 1.0
- 不推荐使用。
- 已停止维护。

View File

@@ -0,0 +1,78 @@
# 语义理解
微信开放平台语义理解接口调用http请求简单方便用户无需掌握语义理解及相关技术只需根据自己的产品特点选择相应的服务即可搭建一套智能语义服务。
## 获取实例
```php
<?php
// ... 前面部分省略
$app = new Application($options);
$semantic = $app->semantic;
```
## API
+ `query($keyword, $categories, $other)` 语义理解:
+ `$keyword` 为关键字
+ `$categories` 需要使用的服务类型,数组或者多个用 “,” 隔开字符吕,不能为空;
+ `$other` 为其它属性:
+ `latitude` `float` 纬度坐标,与经度同时传入;与城市二选一传入
+ `longitude` `float` 经度坐标,与纬度同时传入;与城市二选一传入
+ `city` `string` 城市名称,与经纬度二选一传入
+ `region` `string` 区域名称,在城市存在的情况下可省;与经纬度二选一传入
+ `uid` `string` 用户唯一id非开发者id用户区分公众号下的不同用户建议填入用户openid如果为空则无法使用上下文理解功能。appid和uid同时存在的情况下才可以使用上下文理解功能。
> 单类别意图比较明确识别的覆盖率比较大所以如果只要使用特定某个类别建议将category只设置为该类别。
example:
```php
$result = $semantic->query('查一下明天从北京到上海的南航机票', "flight,hotel", array('city' => '北京', 'uid' => '123456'));
// 查询参数:
// {
// "query":"查一下明天从北京到上海的南航机票",
// "city":"北京",
// "category": "flight,hotel",
// "appid":"wxaaaaaaaaaaaaaaaa",
// "uid":"123456"
// }
```
返回值示例:
```json
{
"errcode":0,
"query":"查一下明天从北京到上海的南航机票",
"type":"flight",
"semantic":{
"details":{
"start_loc":{
"type":"LOC_CITY",
"city":"北京市",
"city_simple":"北京",
"loc_ori":"北京"
},
"end_loc": {
"type":"LOC_CITY",
"city":"上海市",
"city_simple":"上海",
"loc_ori":"上海"
},
"start_date": {
"type":"DT_ORI",
"date":"2014-03-05",
"date_ori":"明天"
},
"airline":"中国南方航空公司"
},
"intent":"SEARCH"
}
```
更多详细内容与协议说明,请查看 [微信官方文档](http://mp.weixin.qq.com/wiki/)

View File

@@ -0,0 +1,167 @@
# 服务端
我们在入门小教程一节以服务端为例讲解了一个基本的消息的处理,这里就不再讲服务器验证的流程了,请直接参考前面的入门实例即可。
服务端的作用呢,在整个微信开发中主要是负责 **[接收用户发送过来的消息](http://mp.weixin.qq.com/wiki/10/79502792eef98d6e0c6e1739da387346.html)**,还有 **[用户触发的一系列事件](http://mp.weixin.qq.com/wiki/2/5baf56ce4947d35003b86a9805634b1e.html)**。
首先我们得厘清一下消息与事件的回复,当你收到用户消息后(消息由微信服务器推送到你的服务器),在你对消息进行一些处理后,不管是选择回复一个消息还是什么不都回给用户,你也应该给微信服务器一个 “答复”如果是选择回复一条消息就直接返回一个消息xml就好如果选择不作任何回复你也得回复一个空字符串或者字符串 `SUCCESS`(不然用户就会看到 `该公众号暂时无法提供服务`)。
## 基本使用
在 SDK 中呢,使用 `setMessageHandler(callable $callback)` 来设置消息处理函数:
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
// 从项目实例中得到服务端应用实例。
$server = $app->server;
$server->setMessageHandler(function ($message) {
// $message->FromUserName // 用户的 openid
// $message->MsgType // 消息类型event, text....
return "您好!欢迎关注我!";
});
$response = $server->serve();
$response->send(); // Laravel 里请使用return $response;
```
这里我们使用 `setMessageHandler` 传入了一个 **闭包([Closure](http://php.net/manual/en/class.closure.php)**,该闭包接收一个参数 `$message` 为消息对象Collection这里需要注意的时与 2.0 不同2.0 当中我们对消息与事件做了区分,还对消息进行了分类(按 MsgType。在 3.0 后,**所有的消息包括事件都会使用 `setMessageHandler` 来处理**,也就是说你可能需要在里面进行一些判断,例如:
```php
$server->setMessageHandler(function ($message) {
switch ($message->MsgType) {
case 'event':
return '收到事件消息';
break;
case 'text':
return '收到文字消息';
break;
case 'image':
return '收到图片消息';
break;
case 'voice':
return '收到语音消息';
break;
case 'video':
return '收到视频消息';
break;
case 'location':
return '收到坐标消息';
break;
case 'link':
return '收到链接消息';
break;
// ... 其它消息
default:
return '收到其它消息';
break;
}
// ...
});
```
当然,因为这里 `setMessageHandler` 接收一个 [`callable`](http://php.net/manual/zh/language.types.callable.php) 的参数,所以你不一定要传入一个 Closure 闭包,你可以选择传入一个函数名,一个 `[$class, $method]` 或者 `Foo::bar` 这样的类型。
> :heart: 注意,默认没有验证是否为微信的请求,部署上线建议关掉 debug 模式。
某些情况,我们需要直接使用 `$message` 参数,那么怎么在 `setMessageHandler` 闭包外调用呢?
```php
$message = $server->getMessage();
```
> 注意:`$message` 是一个数组类型的数据,使用的时候这样使用:`$message['ToUserName']`
## 请求消息的属性
当你接收到用户发来的消息时,可能会提取消息中的相关属性,那么请参考:
请求消息基本属性(以下所有消息都有的基本属性)
$message->ToUserName 接收方帐号(该公众号 ID
$message->FromUserName 发送方帐号OpenID, 代表用户的唯一标识)
$message->CreateTime 消息创建时间(时间戳)
$message->MsgId 消息 ID64位整型
### 文本:
$message->MsgType text
$message->Content 文本消息内容
### 图片:
$message->MsgType image
$message->PicUrl 图片链接
### 语音:
$message->MsgType voice
$message->MediaId 语音消息媒体id可以调用多媒体文件下载接口拉取数据。
$message->Format 语音格式,如 amrspeex 等
$message->Recognition * 开通语音识别后才有
> 请注意开通语音识别后用户每次发送语音给公众号时微信会在推送的语音消息XML数据包中增加一个 `Recongnition` 字段
### 视频:
$message->MsgType video
$message->MediaId 视频消息媒体id可以调用多媒体文件下载接口拉取数据。
$message->ThumbMediaId 视频消息缩略图的媒体id可以调用多媒体文件下载接口拉取数据。
### 小视频:
$message->MsgType shortvideo
$message->MediaId 视频消息媒体id可以调用多媒体文件下载接口拉取数据。
$message->ThumbMediaId 视频消息缩略图的媒体id可以调用多媒体文件下载接口拉取数据。
### 事件:
$message->MsgType event
$message->Event 事件类型 subscribe(订阅)、unsubscribe(取消订阅) ... CLICK 等)
# 扫描带参数二维码事件
$message->EventKey 事件KEY值比如qrscene_123123qrscene_为前缀后面为二维码的参数值
$message->Ticket 二维码的 ticket可用来换取二维码图片
# 上报地理位置事件
$message->Latitude 23.137466 地理位置纬度
$message->Longitude 113.352425 地理位置经度
$message->Precision 119.385040 地理位置精度
# 自定义菜单事件
$message->EventKey 事件KEY值与自定义菜单接口中KEY值对应CUSTOM_KEY_001, www.qq.com
### 地理位置:
$message->MsgType location
$message->Location_X 地理位置纬度
$message->Location_Y 地理位置经度
$message->Scale 地图缩放大小
$message->Label 地理位置信息
### 链接:
$message->MsgType link
$message->Title 消息标题
$message->Description 消息描述
$message->Url 消息链接
## 回复消息
回复的消息可以为 `null`,此时 SDK 会返回给微信一个 "SUCCESS",你也可以回复一个普通字符串,比如:`欢迎关注 overtrue.`,此时 SDK 会对它进行一个封装,产生一个 [`EasyWeChat\Message\Text`](https://github.com/EasyWeChat/message/blob/master/src/Text.php) 类型的消息并在最后的 `$server->serve();` 时生成对应的消息 XML 格式。
如果你想返回一个自己手动拼的原生 XML 格式消息,请返回一个 [`EasyWeChat\Message\Raw`](https://github.com/EasyWeChat/message/blob/master/src/Raw.php) 实例即可。
## 消息转发给客服系统
参见:[多客服消息转发](message-transfer.html)
关于消息的使用,请参考 [`消息`](messages.html) 章节。

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
# 短网址服务
主要使用场景: 开发者用于生成二维码的原链接(商品、支付二维码等)太长导致扫码速度和成功率下降,将原长链接通过此接口转成短链接再生成二维码将大大提升扫码速度和成功率。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$url = $app->url;
```
## API
+ `shorten($url)` 长链接转短链接
example:
```php
$shortUrl = $url->shorten('http://overtrue.me/open-source');
//
```
微信官方文档http://mp.weixin.qq.com/wiki/

View File

@@ -0,0 +1,61 @@
exports = module.exports = [
{
text: '开始使用',
items: [
{ text: '概述', link: '/3.x/overview.html' },
{ text: '安装', link: '/3.x/installation.html' },
{ text: '小教程', link: '/3.x/tutorial.html' },
{ text: '配置', link: '/3.x/configuration.html' },
{ text: '在框架中使用', link: '/3.x/integration.html' },
{ text: '常见问题汇总', link: '/3.x/troubleshooting.html' }
]
},
{
text: '基本使用',
items: [
{ text: '服务端', link: '/3.x/server.html' },
{ text: '消息', link: '/3.x/messages.html' },
{ text: '多客服消息转发', link: '/3.x/message-transfer.html' },
{ text: '事件', link: '/3.x/events.html' },
{ text: '群发消息', link: '/3.x/broadcast.html' },
{ text: '模板消息', link: '/3.x/notice.html' },
{ text: '用户', link: '/3.x/user.html' },
{ text: '用户标签', link: '/3.x/user-tag.html' },
{ text: '用户组', link: '/3.x/user-group.html' },
{ text: '网页授权', link: '/3.x/oauth.html' },
{ text: '素材管理', link: '/3.x/material.html' },
{ text: '菜单', link: '/3.x/menu.html' },
{ text: 'JSSDK', link: '/3.x/js.html' },
{ text: '支付', link: '/3.x/payment.html' },
{ text: '企业支付', link: '/3.x/merchant_payment.html' },
{ text: '红包', link: '/3.x/lucky-money.html' },
{ text: '卡券', link: '/3.x/card.html' },
{ text: '小店', link: '/3.x/store.html' },
{ text: '门店', link: '/3.x/poi.html' },
{ text: '客服', link: '/3.x/staff.html' },
{ text: '数据统计与分析', link: '/3.x/anaylsis.html' },
{ text: '二维码', link: '/3.x/qrcode.html' },
{ text: '短网址', link: '/3.x/short-url.html' },
{ text: '小程序', link: '/3.x/mini_program.html' },
{ text: '语义理解', link: '/3.x/semantic.html' },
{ text: '自动回复', link: '/3.x/reply.html' },
{ text: '开放平台', link: '/3.x/open_platform.html' }
]
},
{
text: '自定义',
items: [
{ text: 'Access Token', link: '/3.x/access_token.html' },
{ text: '缓存', link: '/3.x/cache.html' }
]
},
{
text: '其他',
items: [
{ text: '问题解答', link: '/3.x/troubleshooting.html' },
{ text: '贡献', link: '/3.x/contributing.html' },
{ text: '更新日志', link: '/3.x/releases.html' },
{ text: '路线图', link: '/3.x/roadmap.html' }
]
}
]

View File

@@ -0,0 +1,125 @@
# 客服
> 2016.06.28 已经更新为新版多客服 API
> 请更新到 3.1 版本: composer require "overtrue/wechat:~3.1"
微信的客服才能发送消息或者群发消息,而且还有时效限制,真恶心的说。。。
## 客服管理
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$staff = $app->staff; // 客服管理
```
## API
### 获取所有客服账号列表
```php
$staff->lists();
```
### 获取所有在线的客服账号列表
```php
$staff->onlines();
```
### 添加客服帐号
```php
$staff->create('foo@test', '客服1');
```
### 修改客服帐号
```php
$staff->update('foo@test', '客服1');
```
### 删除客服帐号
```php
$staff->delete('foo@test');
```
### 设置客服帐号的头像
```php
$staff->avatar('foo@test', $avatarPath); // $avatarPath 为本地图片路径,非 URL
```
### 获取客服聊天记录 `NEW`
```php
$staff->records($startTime, $endTime, $pageIndex, $pageSize);
// example: $records = $staff->records('2015-06-07', '2015-06-21', 1, 20);
```
### 主动发送消息给用户
```php
$staff->message($message)->to($openId)->send();
```
> `$message` 为消息对象,请参考:[消息](messages.html)
### 指定客服发送消息
```php
$staff->message($message)->by('account@test')->to($openId)->send();
```
> `$message` 为消息对象,请参考:[消息](messages.html)
## 客服会话控制
> 客服会话为新版 API 功能
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$session = $app->staff_session; // 客服会话管理
```
## 创建会话
```php
$session->create('test1@test', 'OPENID');
```
### 关闭会话
```php
$session->close('test1@test', 'OPENID');
```
### 获取客户会话状态
```php
$session->get('OPENID');
```
### 获取客服会话列表
```php
$session->lists('test1@test');
```
### 获取未接入会话列表
```php
$session->waiters();
```
关于更多客服接口信息请参考微信官方文档http://mp.weixin.qq.com/wiki

View File

@@ -0,0 +1,4 @@
# 门店
TODO

View File

@@ -0,0 +1,126 @@
# 疑难解答
在微信公众平台开发的道路上,遍布着各种大大小小的坑,有的人掉坑里,几经折腾又爬出来了,然后拍拍屁股走人。然而坑还在那里,还会继续有后来人掉进去……
这,是我们不愿看到的。
所以在这里,我们将陆续将微信开发中可能遇到的各种疑难问题进行汇总,并给出对应的解决办法。一般情况下,这些问题都可以对号入座,轻松地解决。但也不排除特殊情况,这时候你遇到的问题与文中某一个症状一致,但文中所给的解决方案并不凑效,这种情况下就需要发挥你自己的智慧,去……折腾了……
我们期待这一版块为各位的开发带来便利,同时也希望各位本着开源、分享的精神对其进行补充和完善,将各种坑一一填小、填平,让微信开发变得不那么痛苦,甚至,变成一件快乐的事……
# 一些服务器基本设施问题:
- 时区不对, 使用命令 `date` 可以在服务器上查看当前时间,如果发现时区不对则需要修改时区:[Setting The Correct Timezone In CentOS And Ubuntu Servers With NTP](https://www.liberiangeek.net/2013/02/setting-the-correct-timezone-in-centos-and-ubuntu-servers-with-ntp/)
- ...
## curl: (60) SSL certificate problem: unable to get local issuer certificate
这是 SSL 证书问题所致,在使用 SDK 调用微信支付等相关的操作时可能会遇到报 “SSL certificate problem: unable to get local issuer certificate” 的错误。
微信公众平台提供的文档中建议对部分较敏感的操作接口使用 https 协议进行访问,例如微信支付和红包等接口中涉及到操作商户资金的一些操作。
wechat SDK 遵循了官方建议,所以在调用这些接口时,除了按照官方文档设置操作证书文件外,还需要保证服务器正确安装了 CA 证书。
1. 下载 CA 证书
你可以从 http://curl.haxx.se/ca/cacert.pem 下载 或者 使用[微信官方提供的证书](https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=4_3)中的 CA 证书 `rootca.pem` 也是同样的效果。
2.`php.ini` 中配置 CA 证书
只需要将上面下载好的 CA 证书放置到您的服务器上某个位置,然后修改 `php.ini``curl.cainfo` 为该路径(**绝对路径!**),重启 `php-fpm` 服务即可。
```
curl.cainfo = /path/to/downloaded/cacert.pem
```
> 注意证书文件**路径为绝对路径**!以自己实际情况为准。
其它修改 HTTP 类源文件的方式是不允许的。
## cURL error 56: SSLRead() return error -9806
目前在 OSX 下,发现使用 HomeBrew 装的 PHP 7.0 有这个问题,解决方案是重新 brew 安装 PHP
```shell
$ brew install homebrew/php/php70 --with-homebrew-openssl --with-homebrew-curl --without-snmp -vvv
```
验证:
```shell
$ php -i | grep 'OpenSSL support'
OpenSSL support => enabled
OpenSSL support => enabled
```
## 支付失败!当前页面的 URL 未注册
这是由于微信支付授权目录未正确配置引起的。此时开发者应该登录微信公众平台,进入**【微信支付】->【开发设置】**进行设置。
1. 公众号可添加3个支付授权目录满足不同应用使用同一个公众号进行支付的业务需求。
2. 正确的**【支付授权目录】**应以 `http://` 或 `https://` 开头,并以正斜杠 `/` 结尾,授权目录所包含的域名**必须经过 ICP 备案**。
3. 支付授权目录需**细化至二级或三级目录**。
4. 所有**实际调起微信支付请求的页面都必须要所配置的支付授权目录之下**。
5. 在开发过程中,也可以使用测试授权目录进行开发测试,此时还**应该将参与测试的个人微信号添加到测试白名单中**,否则将出现对应的错误提示……
> 配置前请先理解**页面**、**目录**、**URL **以及**域名**等几个基本概念,并对自己所使用的框架的路由机制有一个大致了解。这样你才会知道自己正在配置的参数是个啥玩意儿,有什么卵用…… :smile:
## redirect_url 参数错误
这是由于程序使用了**网页授权**而公众号没有正确配置**【网页授权域名】**所致。此时你需要登录[微信公众平台](https://mp.weixin.qq.com/),在【开发】->【接口权限】页面找到**网页授权获取用户基本信息**进行配置并保存。
1. 网页授权域名应该为通过 ICP 备案的有效域名,否则保存时无法通过安全监测。
2. 网页授权域名即程序完成授权获得授权 code 后跳转到的页面的域名,一般情况下为你的业务域名。
3. 网页授权域名配置成功后会立即生效。
4. 公众号的网页授权域名只可配置一个,请合理规划你的业务,否则你会发现……授权域名不够用哈。
## [JSAPI] config: invalid url domain
在使用 JS-SDK 进行开发时,每个页面都需要调用 wx.config() 方法配置 JSPAI 参数。如果没有正确配置 **JSAPI 安全域名**并且开启了调试模式,此时就报此错误。遇到这个问题时,开发者需要登录微信公众平台,进入【公众号设置】->【功能设置】页面,将项目所使用的域名添加至 **【JSAPI 安全域名】**列表中。
1. 一个公众号同时最多可绑定**三个**安全域名,并且这些域名必须为通过 **ICP 备案**的**一级或一级以上**的有效域名。
2. JSAPI 安全域名每个月**限修改三次**,修改任何一个都算,所以,请谨慎操作。
3. 如果需要使用 JSAPI 调起支付功能,则支付目录必须也在所配置的**安全域名之下**,并且需要将支付目录添加至**支付授权目录**。
## token验证失败、向公众号发送消息无任何反应
相信对接公众号一般是微信开发者进行开发过程中最先进行的工作,而在这看似简单的配置操作中,也可能会掉坑里。
最常见的两种情况就如下:
1. 确认你 “**启用**” 了开发模式, token 验证通过不代表启用,保存后也不代表启用。看到红色 “**停用**” 才真正的是启用了。
2. 配置好URL(服务器地址)以及Token(令牌)后,点击保存时提示**token验证失败**,出现这种情况的原因有多种,其中之一便是网络不稳定,所以**可尝试多次保存**,若始终无法通过再排查其它可能因素。
3. 配置保存成功之后,向公众号发送消息无任何反应,自己的消息处理程序也没有被调用的记录(无对应日志)。这种情况下如果你尝试**反复停用和启用服务器配置**,可能突然间惊奇地了现,问题莫名其妙的解决了。
4. 使用在线调试工具的消息接口http://mp.weixin.qq.com/debug/ 只要返回绿色的“**请求成功**”,就代表你的代码没有问题,请**重复上面第3项**再测试。
5. **如果你在用什么本地开发工具,或者什么 ngrok 代理到本机这样的开发方式,那么失败就很正常了,微信服务器到你机器的网络延迟太大(还是用服务器开发吧)。**
> 请开发者理解服务器 TOKEN 验证原理(官方文档有说明)并谨记服务器验证时使用 GET 方式访问,而公众平台向你的服务器发送消息/数据则使用 POST 方式,所以服务器验证成功之后,在某些启用了 CSRF 验证的框架里,接收消息时可能还会遇到 CSRF 相关的问题,请根据自己项目实际情况进行排查。
> 另外有的朋友的 Laravel 里使用了 laravel-debugbar这个组件的原理是在页面输出时在后面添加 HTML 来实现的,所以它会改变我们返回给微信的内容,此时要么卸载,要么禁用掉它。
## Maximum function nesting level of '100' reached, aborting!
在使用了 Xdebug 的环境下可能出现这个问题。这是由于 Xdebug 限制函数嵌套的最大层级数默认为100当嵌套次数达到该值便会触发 Xdebug 跳出嵌套并报此错误。
为避免这个问题,**可以将 Xdebug 的 max_nesting_level 参数适当设置大一些**通常设置为200就可以了当然可根据自己实际情况设置为更大的值
如下,修改 php.ini 配置文件后,重启 Apache 或 php-fpm 服务即可。
```
xdebug.max_nesting_level=200
```

View File

@@ -0,0 +1,178 @@
# 快速开始
在我们已经安装完成后即可很快的开始使用它了当然你还是有必要明白PHP基本知识如命名空间等我这里就不赘述了。
我们以完成服务器端验证与接收响应用户发送的消息为例来演示,首先你有必要了解一下微信交互的运行流程:
```
+-----------------+ +---------------+
+----------+ | | POST/GET/PUT | |
| | ------------------> | | -------------------> | |
| user | | wechat server | | your server |
| | < - - - - - - - - - | | | |
+----------+ | | <- - - - - - - - - - | |
+-----------------+ +---------------+
```
那么我们要做的就是图中 **微信服务器把用户消息转到我们的自有服务器(虚线返回部分)** 后的处理过程。
## 服务端验证
在微信接入开始有一个 “服务器验证” 的过程,这一步呢,其实就是微信服务器向我们服务器发起一个请求(上图实线部分),传了一个名称为 `echostr` 的字符串过来,我们只需要原样返回就好了。
你也知道,微信后台只能填写一个服务器地址,所以 **服务器验证****消息的接收与回复**,都在这一个链接内完成交互。
考虑到这些,我已经把验证这一步给封装到 SDK 里了,你可以完全忽略这一步。
下面我们来配置一个基本的服务端,这里假设我们自己的服务器域名叫 `easywechat.org`,我们在服务器上准备这么一个文件`server.php`:
// server.php
```php
<?php
include __DIR__ . '/vendor/autoload.php'; // 引入 composer 入口文件
use EasyWeChat\Foundation\Application;
$options = [
'debug' => true,
'app_id' => 'your-app-id',
'secret' => 'you-secret',
'token' => 'easywechat',
// 'aes_key' => null, // 可选
'log' => [
'level' => 'debug',
'file' => '/tmp/easywechat.log', // XXX: 绝对路径!!!!
],
//...
];
$app = new Application($options);
$response = $app->server->serve();
// 将响应输出
$response->send(); // Laravel 里请使用return $response;
```
> :heart: 安全模式下请一定要填写 `aes_key`
一个服务端带验证功能的代码已经完成,当然没有对消息做处理,别着急,后面我们再讲。
我们先来分析上面的代码:
```php
<?php
// 这行代码是引入 `composer` 的入口文件,这样我们的类才能正常加载。
include __DIR__ . '/vendor/autoload.php';
// 引入我们的主项目的入口类。
use EasyWeChat\Foundation\Application;
// 一些配置
$options = [...];
// 使用配置来初始化一个项目。
$app = new Application($options);
$response = $app->server->serve();
// 将响应输出
$response->send(); // Laravel 里请使用return $response;
```
最后这一行我有必要详细讲一下:
>1. 我们的 `$app->server->serve()` 就是执行服务端业务了,那么它的返回值呢,是一个 `Symfony\Component\HttpFoundation\Response` 实例。
>2. 我这里是直接调用了它的 `send()` 方法,它就是直接输出了,我们在一些框架就不能直接输出了,那你就直接拿到 Response 实例后做相应的操作即可,比如 Laravel 里你就可以直接 `return $app->server->serve();`
OK, 有了上面的代码,那么请你按 **[微信官方的接入指引](http://mp.weixin.qq.com/wiki/17/2d4265491f12608cd170a95559800f2d.html)** 操作,并相应修改上面的 `$options` 的配置。
> URL 就是我们的 `http://easywechat.org/server.php`,这里我是举例哦,你可不要填写我的域名。
这样点击提交验证就OK了。
> :heart: 请一定要将微信后台的开发者模式 “**启用**” !!!!!!看到红色 “**停用**” 才真正的是启用了。
## 接收 & 回复用户消息
那服务端验证通过了,我们就来试一下接收消息吧。
> 在刚刚上面代码最后一行 `$app->server->serve()->send();` 前面,我们调用 `$app->server` 的 `setMessageHandler()` 方法来注册一个消息处理函数,这里用到了 **[PHP 闭包](http://php.net/manual/zh/functions.anonymous.php)** 的知识,如果你不熟悉赶紧补课去。
```php
// ...
$server->setMessageHandler(function ($message) {
return "您好!欢迎关注我!";
});
$response = $app->server->serve();
// 将响应输出
$response->send(); // Laravel 里请使用return $response;
```
> 注意send() 方法里已经包含 echo 了,请不要再加 echo 在前面。
好吧,打开你的微信客户端,向你的公众号发送任意一条消息,你应该会收到回复:`您好!欢迎关注我!`
> 没有收到回复?看到了“你的公众号暂时无法提供服务” 好,那检查一下你的日志吧,日志在哪儿?我们的配置里写了日志路径了(`'/tmp/easywechat.log'`)。 没有这个文件?看看权限哦。
一个基本的服务端验证就完成了。
## 总结
1. 所有的服务都通过主入口 `EasyWeChat\Foundation\Application` 类来获取:
```php
$app = new Application($options);
// services...
$server = $app->server;
$user = $app->user;
$oauth = $app->oauth;
// ... js/menu/staff/material/qrcode/notice/stats...
```
2. 所有的 API 返回值均为 [`EasyWeChat\Support\Collection`](https://github.com/EasyWeChat/support/blob/master/src/Collection.php) 类,这个类是个什么东西呢?
它实现了一些 **[PHP预定义接口](http://php.net/manual/zh/reserved.interfaces.php)**,比如:[`ArrayAccess`](http://php.net/manual/zh/class.arrayaccess.php)、[`Serializable`](http://php.net/manual/zh/class.serializable.php) 等。
有啥好处呢?它让我们操作起返回值来更方便,比如:
```php
$userService = $app->user; // 用户API
$user = $userService->get($openId);
// $user 便是一个 EasyWeChat\Support\Collection 实例
$user['nickname'];
$user->nickname;
$user->get('nickname');
//...
```
还有这些方便的操作:检查是否存在某个属性 `$user->has('email')`、元素个数 `$user->count()`,还有返回数组 `$user->toArray()` ,生成 JSON `$user->toJSON()` 等。
## 最后
希望你在使用本 SDK 的时候能忘记微信官方给你的痛苦,同时如果你发现 SDK 的不足,欢迎提交 PR 或者给我[提建议 & 报告问题](https://github.com/overtrue/wechat/issues)。
祝你生活愉快!

View File

@@ -0,0 +1,112 @@
# 用户组
用户组的使用就非常简单了,基本的增删改查。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$group = $app->user_group; // $user['user_group']
```
## API
### 获取所有分组
```php
$group->lists();
```
example:
```php
$groups = $group->lists();
// {
// "groups": [
// {
// "id": 0,
// "name": "未分组",
// "count": 72596
// },
// {
// "id": 1,
// "name": "黑名单",
// "count": 36
// },
// ...
// ]
// }
var_dump($groups->groups[0]['name']) // “未分组”
```
### 创建分组
```php
$group->create($name);
```
example:
```php
$group->create($name);
```
### 修改分组信息
```php
$group->update($groupId, $name);
```
example:
```php
$group->update($groupId, "新的组名");
```
### 删除分组
```php
$group->delete($groupId);
```
example:
```php
$group->delete($groupId);
```
### 移动单个用户到指定分组
```php
$group->moveUser($openId, $groupId);
```
example:
```php
$group->moveUser($openId, $groupId);
```
### 批量移动用户到指定分组
```php
$group->moveUsers(array $openIds, $groupId);
```
example:
```php
$openIds = [$openId1, $openId2, $openId3 ...];
$group->moveUsers($openIds, $groupId);
```
关于用户管理请参考微信官方文档http://mp.weixin.qq.com/wiki/ `用户管理` 章节。

View File

@@ -0,0 +1,130 @@
# 用户标签
用户标签的使用就非常简单了,基本的增删改查。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$tag = $app->user_tag; // $user['user_tag']
```
## API
### 获取所有标签
```php
$tag->lists();
```
example:
```php
$tags = $tag->lists();
// {
// "tags": [
// {
// "id": 0,
// "name": "标签1",
// "count": 72596
// },
// {
// "id": 1,
// "name": "标签2",
// "count": 36
// },
// ...
// ]
// }
var_dump($tags->tags[0]['name']) // “标签1”
```
### 创建标签
```php
$tag->create($name);
```
example:
```php
$tag->create('测试标签');
```
### 修改标签信息
```php
$tag->update($tagId, $name);
```
example:
```php
$tag->update(12, "新的名称");
```
### 删除标签
```php
$tag->delete($tagId);
```
example:
```php
$tag->delete($tagId);
```
### 获取指定 openid 用户身上的标签
```php
$userTags = $tag->userTags($openId);
//
// {
// "tagid_list":["标签1","标签2"]
// }
```
### 获取标签下粉丝列表
```php
$tag->usersOfTag($tagId, $nextOpenId = '');
// $nextOpenId第一个拉取的OPENID不填默认从头开始拉取
// {
// "count":2,//这次获取的粉丝数量
// "data":{//粉丝列表
// "openid":[
// "ocYxcuAEy30bX0NXmGn4ypqx3tI0",
// "ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"
// ]
// },
// "next_openid":"ocYxcuBt0mRugKZ7tGAHPnUaOW7Y"//拉取列表最后一个用户的openid
// }
```
### 批量为用户打标签
```php
$openIds = [$openId1, $openId2, ...];
$tag->batchTagUsers($openIds, $tagId);
```
### 批量为用户取消标签
```php
$openIds = [$openId1, $openId2, ...];
$tag->batchUntagUsers($openIds, $tagId);
```
关于用户管理请参考微信官方文档http://mp.weixin.qq.com/wiki/ `用户管理` 章节。

View File

@@ -0,0 +1,99 @@
# 用户
用户信息的获取是微信开发中比较常用的一个功能了,以下所有的用户信息的获取与更新,都是**基于微信的 `openid` 的,并且是已关注当前账号的**,其它情况可能无法正常使用。
## 获取实例
```php
<?php
use EasyWeChat\Foundation\Application;
// ...
$app = new Application($options);
$userService = $app->user;
```
## API 列表
### 获取用户信息
```php
$userService->get($openId);
$userService->batchGet($openIds);
```
获取单个:
```php
$user = $userService->get($openId);
echo $user->nickname; // or $user['nickname']
```
获取多个:
```php
$users = $userService->batchGet([$openId1, $openId2, ...]);
```
### 获取用户列表
```php
$userService->lists($nextOpenId = null); // $nextOpenId 可选
```
example:
```php
$users = $userService->lists();
// result
{
"total": 2,
"count": 2,
"data": {
"openid": [
"",
"OPENID1",
"OPENID2"
]
},
"next_openid": "NEXT_OPENID"
}
$users->total; // 2
```
### 修改用户备注
```php
$userService->remark($openId, $remark); // 成功返回boolean
```
example:
```php
$userService->remark($openId, "僵尸粉");
```
### 获取用户所属用户组ID
```php
$userService->group($openId);
```
example:
```php
$userGroupId = $userService->group($openId);
```
## 其它
- [用户标签](user-tag.html)
- [用户分组](user-group.html)
关于用户管理请参考微信官方文档http://mp.weixin.qq.com/wiki/ `用户管理` 章节。

View File

@@ -0,0 +1,81 @@
# 内容安全接口
## 文本安全内容检测
用于校验一段文本是否含有违法内容。
### 频率限制
单个appid调用上限为2000次/分钟1,000,000次/天
### 调用示例
```php
// 传入要检测的文本内容长度不超过500K字节
$content = '你好';
$result = $app->content_security->checkText($content);
// 正常返回 0
{
"errcode": "0",
"errmsg": "ok"
}
//当 $content 内含有敏感信息,则返回 87014
{
"errcode": 87014,
"errmsg": "risky content"
}
```
## 图片安全内容检测
用于校验一张图片是否含有敏感信息。如涉黄、涉及敏感人脸(通常是政治人物)。
### 频率限制
单个appid调用上限为1000次/分钟100,000次/天
### 调用示例
```php
// 所传参数为要检测的图片文件的绝对路径图片格式支持PNG、JPEG、JPG、GIF, 像素不超过 750 x 1334同时文件大小以不超过 300K 为宜,否则可能报错
$result = $app->content_security->checkImage('/path/to/the/image');
// 正常返回 0
{
"errcode": "0",
"errmsg": "ok"
}
// 当图片文件内含有敏感内容,则返回 87014
{
"errcode": 87014,
"errmsg": "risky content"
}
```
## 重要说明
目前上述两个接口仅支持在小程序中使用,示例中的 `$app` 表示小程序实例,即:
```php
use EasyWeChat\Factory;
$config = [
'app_id' => 'wx3cf0f39249eb0exx',
'secret' => 'f1c242f4f28f735d4687abb469072axx',
// 下面为可选项
// 指定 API 调用返回结果的类型array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => __DIR__.'/wechat.log',
],
];
$app = Factory::miniProgram($config);
```

View File

@@ -0,0 +1,49 @@
# JSSDK
微信 JSSDK 官方文档https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
## API
#### 获取JSSDK的配置数组
```php
$app->jssdk->buildConfig(array $APIs, $debug = false, $beta = false, $json = true);
```
默认返回 JSON 字符串,当 `$json``false` 时返回数组,你可以直接使用到网页中。
#### 设置当前URL
```php
$app->jssdk->setUrl($url)
```
如果不想用默认读取的URL可以使用此方法手动设置通常不需要。
#### 示例
我们可以生成js配置文件
```js
<script src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
wx.config(<?php echo $app->jssdk->buildConfig(array('updateAppMessageShareData', 'updateTimelineShareData'), true) ?>);
</script>
```
结果如下:
```js
<script src="https://res.wx.qq.com/open/js/jweixin-1.4.0.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
wx.config({
debug: true, // 请在上线前删除它
appId: 'wx3cf0f39249eb0e60',
timestamp: 1430009304,
nonceStr: 'qey94m021ik',
signature: '4F76593A4245644FAE4E1BC940F6422A0C3EC03E',
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData']
});
</script>
```

View File

@@ -0,0 +1,87 @@
# 临时素材
上传的临时多媒体文件有格式和大小限制,如下:
> - 图片image: 2M支持 `JPG` 格式
> - 语音voice2M播放长度不超过 `60s`,支持 `AMR\MP3` 格式
> - 视频video10MB支持 `MP4` 格式
> - 缩略图thumb64KB支持 `JPG` 格式
## 上传图片
> 注意:微信图片上传服务有敏感检测系统,图片内容如果含有敏感内容,如色情,商品推广,虚假信息等,上传可能失败。
```php
$app->media->uploadImage($path);
```
## 上传声音
```php
$app->media->uploadVoice($path);
```
## 上传视频
```php
$app->media->uploadVideo($path, $title, $description);
```
## 上传缩略图
用于视频封面或者音乐封面。
```php
$app->media->uploadThumb($path);
```
## 上传群发视频
上传视频获取 `media_id` 用以创建群发消息用。
```php
$app->media->uploadVideoForBroadcasting($path, $title, $description);
//{
// "media_id": "rF4UdIMfYK3efUfyoddYRMU50zMiRmmt_l0kszupYh_SzrcW5Gaheq05p_lHuOTQ",
// "title": "TITLE",
// "description": "Description"
//}
```
## 创建群发消息
不要与上面 **上传群发视频** 搞混了,上面一个是上传视频得到 `media_id`,这个是使用该 `media_id` 加标题描述 **创建一条消息素材** 用来发送给用户。详情参见:[消息群发](../official-account/broadcasting.md)
```php
$app->media->createVideoForBroadcasting($mediaId, $title, $description);
//{
// "type":"video",
// "media_id":"IhdaAQXuvJtGzwwc0abfXnzeezfO0NgPK6AQYShD8RQYMTtfzbLdBIQkQziv2XJc",
// "created_at":1398848981
//}
```
## 获取临时素材内容
比如图片、语音等二进制流内容,响应为 `EasyWeChat\Kernel\Http\StreamResponse` 实例。
```php
$stream = $app->media->get($mediaId);
if ($stream instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
// 以内容 md5 为文件名存到本地
$stream->save('保存目录');
// 自定义文件名,不需要带后缀
$stream->saveAs('保存目录', '文件名');
}
```
## 获取 JSSDK 上传的高清语音
```php
$stream = $app->media->getJssdkMedia($mediaId);
$stream->saveAs('保存目录', 'custom-name.speex');
```

View File

@@ -0,0 +1,47 @@
# 二维码
目前有 2 种类型的二维码:
1. 临时二维码,是有过期时间的,最长可以设置为在二维码生成后的 **30天**后过期,但能够生成较多数量。临时二维码主要用于帐号绑定等不要求二维码永久保存的业务场景
2. 永久二维码是无过期时间的但数量较少目前为最多10万个。永久二维码主要用于适用于帐号绑定、用户来源统计等场景。
## 创建临时二维码
```php
$result = $app->qrcode->temporary('foo', 6 * 24 * 3600);
// Array
// (
// [ticket] => gQFD8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTmFjVTRWU3ViUE8xR1N4ajFwMWsAAgS2uItZAwQA6QcA
// [expire_seconds] => 518400
// [url] => http://weixin.qq.com/q/02NacU4VSubPO1GSxj1p1k
// )
```
## 创建永久二维码
```php
$result = $app->qrcode->forever(56);// 或者 $app->qrcode->forever("foo");
// Array
// (
// [ticket] => gQFD8TwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyTmFjVTRWU3ViUE8xR1N4ajFwMWsAAgS2uItZAwQA6QcA
// [url] => http://weixin.qq.com/q/02NacU4VSubPO1GSxj1p1k
// )
```
## 获取二维码网址
```php
$url = $app->qrcode->url($ticket);
// https://api.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
```
## 获取二维码内容
```php
$url = $app->qrcode->url($ticket);
$content = file_get_contents($url); // 得到二进制图片内容
file_put_contents(__DIR__ . '/code.jpg', $content); // 写入文件
```

View File

@@ -0,0 +1,15 @@
# 短网址服务
主要使用场景: 开发者用于生成二维码的原链接(商品、支付二维码等)太长导致扫码速度和成功率下降,将原长链接通过此接口转成短链接再生成二维码将大大提升扫码速度和成功率。
## 长链接转短链接
```php
$shortUrl = $app->url->shorten('https://easywechat.com');
//
(
[errcode] => 0
[errmsg] => ok
[short_url] => https://w.url.cn/s/Aq7jWrd
)
```

View File

@@ -0,0 +1,56 @@
# 贡献代码
## 开发
我们欢迎广大开发者贡献大家的智慧,让我们共同让它变得更完美.
### 开始之前
请严格遵循以下代码标准:
> - [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md).
> - 使用 4 个空格作为缩进。
### 流程
1. Fork [overtrue/wechat](https://github.com/overtrue/wechat) 到本地.
2. 创建新的分支:
```shell
$ git checkout -b new_feature
```
3. 编写代码。
4. Push 到你的分支:
```shell
$ git push origin new_feature
```
5. 创建 Pull Request 并描述你完成的功能或者做出的修改。
> 注意:注释请使用英文
## 更新文档
我们的文档也是开源的,源代码在 [w7corp/EasyWeChat/docs](https://github.com/w7corp/easywechat/tree/master/docs)。
### 流程
1. Fork [w7corp/EasyWeChat](https://github.com/w7corp/easywechat)
2. Clone 到你的电脑:
```shell
$ git clone https://github.com/<username>/site.git
$ cd docs
```
3. 创建新的分支,编辑文档
4. Push 到你的分支。
5. 创建 Pull Request 并描述你完成的功能或者做出的修改。
## 报告 Bug
当你在使用过程中遇到问题,请查阅 [疑难解答](troubleshooting.html) 或者在这里提问 [GitHub](https://github.com/overtrue/wechat/issues). 如果还是不能解决你的问题,请到 GitHub 联系我们。
[overtrue/wechat]: https://github.com/overtrue/wechat

View File

@@ -0,0 +1,33 @@
# Access Token
我们一个 SDK 应用在初始化以后,你可以在任何时机从应用中拿到该配置下的 Access Token 实例:
```php
use EasyWeChat\Factory;
$config = [
//...
];
$app = Factory::officialAccount($config);
// 获取 access token 实例
$accessToken = $app->access_token;
$token = $accessToken->getToken(); // token 数组 token['access_token'] 字符串
$token = $accessToken->getToken(true); // 强制重新从微信服务器获取 token.
```
## 修改 `$app` 的 Access Token
```php
$app['access_token']->setToken($newAccessToken, 7200);
```
例如:
```php
$app['access_token']->setToken('ccfdec35bd7ba359f6101c2da321d675');
// 或者指定过期时间
$app['access_token']->setToken('ccfdec35bd7ba359f6101c2da321d675', 3600); // 单位:秒
```

View File

@@ -0,0 +1,159 @@
# 缓存
本项目使用 [symfony/cache](https://github.com/symfony/cache) 来完成缓存工作,它支持基本目前所有的缓存引擎。
在我们的 SDK 中的所有缓存默认使用文件缓存,缓存路径取决于 PHP 的临时目录,如果你需要自定义缓存,那么你需要做如下的事情:
你可以参考[symfony/cache官方文档](https://symfony.com/doc/current/components/cache.html) 来替换掉应用中默认的缓存配置:
## 以 redis 为例
### Symfony 4.3 +
> 请先安装 redis 拓展:`composer require predis/predis`
```php
use Symfony\Component\Cache\Adapter\RedisAdapter;
// 创建 redis 实例
$client = new \Predis\Client('tcp://10.0.0.1:6379');
// 创建缓存实例
$cache = new RedisAdapter($client);
// 替换应用中的缓存
$app->rebind('cache', $cache);
```
### Symfony 3.4 +
> 请先安装 redis 拓展https://github.com/phpredis/phpredis
```php
use Symfony\Component\Cache\Simple\RedisCache;
// 创建 redis 实例
$redis = new Redis();
$redis->connect('redis_host', 6379);
// 创建缓存实例
$cache = new RedisCache($redis);
// 替换应用中的缓存
$app->rebind('cache', $cache);
```
### Laravel 中使用
在 Laravel 中框架使用 [predis/predis](https://github.com/nrk/predis)
### Symfony 4.3 +
> 请先安装 redis 拓展:`composer require predis/predis`
```php
use Symfony\Component\Cache\Adapter\RedisAdapter;
// 创建缓存实例
$cache = new RedisAdapter(app('redis')->connection()->client());
$app->rebind('cache', $cache);
```
### Symfony 3.4 +
```php
use Symfony\Component\Cache\Simple\RedisCache;
$predis = app('redis')->connection()->client(); // connection($name), $name 默认为 `default`
$cache = new RedisCache($predis);
$app->rebind('cache', $cache);
```
> 上面提到的 `app('redis')->connection($name)`, 这里的 `$name` 是 laravel 项目中配置文件 `database.php` 中 `redis` 配置名 `default`https://github.com/laravel/laravel/blob/master/config/database.php#L118
> 如果你使用的其它连接,对应传名称就好了。
## 使用自定义的缓存方式
如果你发现 symfony 提供的十几种缓存方式都满足不了你的需求的话,那么你可以自己建立一个类来完成缓存操作,前提这个类得实现接口:[PSR-16](http://www.php-fig.org/psr/psr-16/)
该接口有以下方法需要实现:
```php
public function get($key, $default = null);
public function set($key, $value, $ttl = null);
public function delete($key);
public function clear();
public function getMultiple($keys, $default = null);
public function setMultiple($values, $ttl = null);
public function deleteMultiple($keys);
public function has($key);
```
下面为一个示例:
```php
<?php
use Psr\SimpleCache\CacheInterface;
class MyCustomCache implements CacheInterface
{
public function get($key, $default = null)
{
// your code
}
public function set($key, $value, $ttl = null)
{
// your code
}
public function delete($key)
{
// your code
}
public function clear()
{
// your code
}
public function getMultiple($keys, $default = null)
{
// your code
}
public function setMultiple($values, $ttl = null)
{
// your code
}
public function deleteMultiple($keys)
{
// your code
}
public function has($key)
{
// your code
}
}
```
然后实例化你的缓存类并在 EasyWeChat 里使用它:
```php
$app->rebind('cache', new MyCustomCache());
```
OK这样就完成了自定义缓存的操作。

View File

@@ -0,0 +1,14 @@
# 自定义服务模块
由于使用了容器模式来组织各模块的实例,意味着你可以比较容易的替换掉已经有的服务,以公众号服务为例:
```php
<...>
$app = Factory::officialAccount($config);
$app->rebind('request', new MyCustomRequest(...));
```
这里的 `request` 为 SDK 内部服务名称。

View File

@@ -0,0 +1,26 @@
> 👋🏼 您当前浏览的文档为 4.x其它版本的文档请参考[6.x](/6.x/)、[5.x](/5.x/)、[3.x](/3.x/)
# EasyWeChat
EasyWeChat 是一个开源的 [微信](http://www.wechat.com) 非官方 SDK。安装非常简单因为它是一个标准的 [Composer](https://getcomposer.org/) 包,这意味着任何满足下列安装条件的 PHP 项目支持 Composer 都可以使用它。
## 环境要求
> - PHP >= 7.0
> - [PHP cURL 扩展](http://php.net/manual/en/book.curl.php)
> - [PHP OpenSSL 扩展](http://php.net/manual/en/book.openssl.php)
> - [PHP SimpleXML 扩展](http://php.net/manual/en/book.simplexml.php)
> - [PHP fileinfo 拓展](http://php.net/manual/en/book.fileinfo.php)
Laravel 5 拓展包: [overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
# 参与贡献
1. fork 当前库到你的名下
2. 选择你想要修改的语言版本,`zh-CN` 或者 `en`
3. 在你的本地修改完成审阅过后提交到你的仓库
4. 提交 PR 并描述你的修改,等待合并
# License
MIT

View File

@@ -0,0 +1,20 @@
# 安装
## 环境要求
> - PHP >= 7.0
> - [PHP cURL 扩展](http://php.net/manual/en/book.curl.php)
> - [PHP OpenSSL 扩展](http://php.net/manual/en/book.openssl.php)
> - [PHP SimpleXML 扩展](http://php.net/manual/en/book.simplexml.php)
> - [PHP fileinfo 拓展](http://php.net/manual/en/book.fileinfo.php)
Laravel 5 拓展包: [overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
## 安装
使用 [composer](http://getcomposer.org/):
```shell
$ composer require overtrue/wechat:~4.0 -vvv
```

View File

@@ -0,0 +1,34 @@
# 在框架中使用
EasyWeChat 是一个通用的 Composer 包,所以不需要对框架单独做修改,只要支持 Composer 就能直接使用,当然了,为了更方便的使用,我们收集了以下框架单独提供的拓展包:
## Laravel
> - [overtrue/laravel-wechat](https://github.com/overtrue/laravel-wechat)
## Symfony
> - [lilocon/WechatBundle](https://github.com/lilocon/WechatBundle)
## Yii
> - [jianyan74/yii2-easy-wechat](https://github.com/jianyan74/yii2-easy-wechat) 适用于 EasyWeChat 4.x
> - [max-wen/yii2-easy-wechat](https://github.com/max-wen/yii2-easy-wechat) 适用于 EasyWeChat 3.x
## ThinkPHP
> - [naixiaoxin/think-wechat](https://github.com/qiqizjl/think-wechat) 适用于 EasyWeChat 4.x
> - [zyan/think-wechat](https://github.com/aa24615/think-wechat) 适用于 EasyWeChat 4.x/5.x
## CI
TODO
## Phalcon
TODO
... more

View File

@@ -0,0 +1,15 @@
# 获取平台证书
调用获取平台证书接口之前请前往微信支付商户平台升级API证书升级后才可成功调用本接口。
```php
// 获取到证书后可以做缓存处理,无需每次重新获取
$response = $app->certficates->get(bool $returnRaw = false);
// 获取到平台证书后,可以直接使用 setCertificate 方法把证书配置追加到配置项里面去
$app->setCertificate(string $certificate, string $serialNo);
```
> $returnRaw 不填默认为false时请确保你的PHP已安装了sodium扩展
> 返回值固定array格式的解密后的证书信息
> $returnRaw 传入true时
> 返回值Response对象`$response->getBody()->getContents();`获取到微信返回xml原始数据

View File

@@ -0,0 +1,52 @@
# 小微商户
你在阅读本文之前确认你已经仔细阅读了:[微信小微商户专属接口文档](https://pay.weixin.qq.com/wiki/doc/api/xiaowei.php?chapter=19_2)。
PS: ⚠️ 因系统升级腾讯暂时关闭了小微商户接口恢复时间未定。调用提交申请接口会提示「PARAM_ERROR」详细说明可参见[微信开放平台相关帖子](https://developers.weixin.qq.com/community/develop/doc/0000a0ffc9ce28bd4bc9999ba5b800)
## 配置
小微商户整体接口调用方式相对于其他微信接口略有不同,配置时请勿填错,相关配置如下:
```php
use EasyWeChat\Factory;
$config = [
// 必要配置
'mch_id' => 'your-mch-id', // 服务商的商户号
'key' => 'key-for-signature', // API 密钥
'apiv3_key' => 'APIv3-key-for-signature', // APIv3 密钥
// API 证书路径(登录商户平台下载 API 证书)
'cert_path' => 'path/to/your/cert.pem', // XXX: 绝对路径!!!!
'key_path' => 'path/to/your/key', // XXX: 绝对路径!!!!
// 以下两项配置在获取证书接口时可为空,在调用入驻接口前请先调用获取证书接口获取以下两项配置,如果获取过证书可以直接在这里配置,也可参照本文档获取平台证书章节中示例
// 'serial_no' => '获取证书接口获取到的平台证书序列号',
// 'certificate' => '获取证书接口获取到的证书内容'
// 以下为可选项
// 指定 API 调用返回结果的类型array(default)/collection/object/raw/自定义类名
'response_type' => 'array'
'appid' => 'wx931386123456789e' // 服务商的公众账号 ID
];
$app = Factory::microMerchant($config);
```
`$app` 在所有相关小微商户的文档都是指 `Factory::microMerchant` 得到的实例,就不在每个页面单独写了。
## 使用时值得注意的地方:
1、小微商户所有接口中以下列出参数 `version`, `mch_id`, `nonce_str`, `sign`, `sign_type`, `cert_sn` 可不用传入。
2、所有敏感信息无需手动加密sdk会在调用接口前自动完成加密
3、在调用入驻等需要敏感信息加密的接口前请先调用获取证书接口然后把配置填入配置项
4、入驻成功获取到子商户号后需帮助子商户调用配置修改等接口可以先调用以下方法方便调用修改等接口时无需再次传入子商户号
```php
// $subMchId 为子商户号
// $appid 服务商的公众账号 ID
$app->setSubMchId(string $subMchId, string $appId = '');
```

View File

@@ -0,0 +1,24 @@
# 商户信息修改
## 修改结算银行卡
```php
$response = $app->material->setSettlementCard([
// 'sub_mch_id' => '1230000109',
'account_number' => '银行卡号',
'bank_name' => '开户银行全称(含支行)',
'account_bank' => '开户银行',
'bank_address_code' => '开户银行省市编码',
]);
```
## 修改联系信息
```php
$response = $app->material->updateContact([
// 'sub_mch_id' => '1230000109',
'mobile_phone' => '手机号',
'email' => '邮箱',
'merchant_name' => '商户简称',
]);
```
> 以上接口调用过 `setSubMchId` 方法则无需传入 `sub_mch_id` 参数

View File

@@ -0,0 +1,7 @@
# 图片上传
上传证件照片。支持 jpeg、jpg、bmp、png 格式图片大小不超过2M。
```php
// $path string 图片路径
$response = $app->media->upload($path);
```

View File

@@ -0,0 +1,28 @@
# 小微商户配置
## 关注功能配置
```php
$response = $app->merchantConfig->setFollowConfig(string $subAppId, string $subscribeAppId, string $receiptAppId = '', string $subMchId = '');
```
> 注意:`subscribe_appid``receipt_appid` 两个参数二选一两个都填的话SDK默认选第一个具体请参考小微商户专属文档
## 开发配置新增支付目录
```php
$response = $app->merchantConfig->addPath(string $jsapiPath, string $appId = '', string $subMchId = '');
```
## 新增对应APPID关联
```php
$response = $app->merchantConfig->bindAppId(string $subAppId, string $appId = '', string $subMchId = '');
```
## 开发配置查询
```php
$response = $app->merchantConfig->getConfig(string $subMchId = '', string $appId = '');
```
> 以上接口调用过 `setSubMchId` 方法并且两个参数都传入过 则无需传入 `sub_mch_id``appid` 参数

View File

@@ -0,0 +1,26 @@
# 商户入驻
## 申请入驻
使用申请入驻接口提交你的小微商户资料。
```php
$result = $app->submitApplication([
'business_code' => '123456', // 业务申请编号
'id_card_copy' => 'media_id', // 身份证人像面照片
// ...
// 参数太多就不一一列出,自行根据 (小微商户专属文档 -> 申请入驻api) 填写
]);
```
## 查询申请状态
使用申请入驻接口提交小微商户资料后一般5分钟左右可以通过该查询接口查询具体的申请结果。
```php
$applymentId = '商户申请单号(applyment_id 申请入驻接口返回)';
$businessCode = '业务申请编号(business_code)';
$app->getStatus(string $applymentId, string $businessCode = '');
```
> 商户申请单号和业务申请编号填写一个就行了,当 `applyment_id` 已填写时,`business_code` 字段无效。
当查询申请状态为待签约,接口会一并返回签约二维码,服务商需引导商户使用本人微信扫码完成协议签署。

View File

@@ -0,0 +1,27 @@
# 商户升级
## 提交升级申请单
使用“提交升级申请单”接口为小微商户发起升级流程,根据商户实际情况可升级为个体户、企业、党政、机关及事业单位、其他组织。。
```php
$result = $app->upgrade([
'organization_type' => '2', // 主体类型
'business_license_copy' => 'media_id', // 营业执照扫描件
// ...
// 参数太多就不一一列出,自行根据 (小微商户专属文档 -> 提交升级申请单API) 填写
]);
```
## 查询升级申请单状态
使用“提交升级申请单”接口后可不定期调用此接口查询申请单状态建议提交申请后1分钟查询直至申请单为“完成”状态。
1)若申请状态为待账户验证,请按接口中的指引完成账户验证
2)若申请状态为审核中微信支付会在2个工作日内完成资料审核
3)若申请状态为待签约,接口会返回签约二维码
```php
$app->getUpgradeStatus(string $subMchId = '');
```
> 调用该接口前调用过 `setSubMchId` 方法则无需传入 `$subMchId` 参数

View File

@@ -0,0 +1,14 @@
# 提现相关
## 查询提现状态
```php
$response = $app->withdraw->queryWithdrawalStatus($date, $subMchId = '');
```
## 重新发起提现
```php
$response = $app->withdraw->requestWithdraw($date, $subMchId = '');
```
> 以上接口调用过 `setSubMchId` 方法则无需传入 `sub_mch_id` 参数

View File

@@ -0,0 +1,109 @@
# 小程序码
## 获取小程序码
### 接口A: 适用于需要的码数量较少的业务场景
API:
```
$app->app_code->get(string $path, array $optional = []);
```
其中 `$optional` 为以下可选参数:
> - **width** Int - 默认 430 二维码的宽度
> - **auto_color** 默认 false 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调
> - **line_color** 数组,`auto_color` 为 `false` 时生效,使用 rgb 设置颜色 例如 ,示例:`["r" => 0,"g" => 0,"b" => 0]`。
示例代码:
```php
$response = $app->app_code->get('path/to/page');
// 或者
$response = $app->app_code->get('path/to/page', [
'width' => 600,
//...
]);
// 或者指定颜色
$response = $app->app_code->get('path/to/page', [
'width' => 600,
'line_color' => [
'r' => 105,
'g' => 166,
'b' => 134,
],
]);
// $response 成功时为 EasyWeChat\Kernel\Http\StreamResponse 实例,失败时为数组或者你指定的 API 返回格式
// 保存小程序码到文件
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->save('/path/to/directory');
}
// 或
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->saveAs('/path/to/directory', 'appcode.png');
}
```
### 接口B适用于需要的码数量极多或仅临时使用的业务场景
API:
```
$app->app_code->getUnlimit(string $scene, array $optional = []);
```
> 其中 $scene 必填,$optinal 与 get 方法一致,多一个 page 参数。
示例代码:
```php
$response = $app->app_code->getUnlimit('scene-value', [
'page' => 'path/to/page',
'width' => 600,
]);
// $response 成功时为 EasyWeChat\Kernel\Http\StreamResponse 实例,失败为数组或你指定的 API 返回类型
// 保存小程序码到文件
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->save('/path/to/directory');
}
// 或
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->saveAs('/path/to/directory', 'appcode.png');
}
```
## 获取小程序二维码
API:
```
$app->app_code->getQrCode(string $path, int $width = null);
```
> 其中 $path 必填,其余参数可留空。
示例代码:
```php
$response = $app->app_code->getQrCode('/path/to/page');
// $response 成功时为 EasyWeChat\Kernel\Http\StreamResponse 实例,失败为数组或你指定的 API 返回类型
// 保存小程序码到文件
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->save('/path/to/directory');
}
// 或
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $response->saveAs('/path/to/directory', 'appcode.png');
}
```
##

View File

@@ -0,0 +1,9 @@
# 微信登录
## 根据 jsCode 获取用户 session 信息
API:
```php
$app->auth->session(string $code);
```

View File

@@ -0,0 +1,10 @@
# 客服消息
## 获取实例
```php
$service = $app->customer_service;
```
> 使用方法详看公众号-客服消息章节。

View File

@@ -0,0 +1,22 @@
# 数据统计与分析
获取小程序概况趋势:
```php
$app->data_cube->summaryTrend('20170313', '20170313')
```
开始日期与结束日期的格式为 yyyymmdd。
## API
> - `summaryTrend(string $from, string $to);` 概况趋势
> - `dailyVisitTrend(string $from, string $to);` 访问日趋势
> - `weeklyVisitTrend(string $from, string $to);` 访问周趋势
> - `monthlyVisitTrend(string $from, string $to);` 访问月趋势
> - `visitDistribution(string $from, string $to);` 访问分布
> - `dailyRetainInfo(string $from, string $to);` 访问日留存
> - `weeklyRetainInfo(string $from, string $to);` 访问周留存
> - `monthlyRetainInfo(string $from, string $to);` 访问月留存
> - `visitPage(string $from, string $to);` 访问页面
> - `userPortrait(string $from, string $to);` 用户画像分布数据

View File

@@ -0,0 +1,9 @@
# 微信小程序消息解密
## 比如获取电话等功能,信息是加密的,需要解密。
API:
```php
$decryptedData = $app->encryptor->decryptData($session, $iv, $encryptedData);
```

View File

@@ -0,0 +1,121 @@
# 物流助手 电子面单
## 获取支持的快递公司列表
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.getAllDelivery.html
```php
$app->express->listProviders();
{
"count": 8,
"data": [
{
"delivery_id": "BEST",
"delivery_name": "百世快递"
},
...
]
}
```
## 生成运单
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.addOrder.html
```php
$app->express->createWaybill($data);
// 成功返回
{
"order_id": "01234567890123456789",
"waybill_id": "123456789",
"waybill_data": [
{
"key": "SF_bagAddr",
"value": "广州"
},
{
"key": "SF_mark",
"value": "101- 07-03 509"
}
]
}
// 失败返回
{
"errcode": 9300501,
"errmsg": "delivery logic fail",
"delivery_resultcode": 10002,
"delivery_resultmsg": "客户密码不正确"
}
```
## 取消运单
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.cancelOrder.html
```php
$app->express->deleteWaybill($data);
```
## 获取运单数据
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.getOrder.html
```php
$app->express->getWaybill($data);
```
## 查询运单轨迹
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.getPath.html
```php
$app->express->getWaybillTrack($data);
```
## 获取电子面单余额。
仅在使用加盟类快递公司时,才可以调用。
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.getQuota.html
```php
$app->express->getBalance($deliveryId, $bizId);
// 例如:
$app->express->getBalance('YTO', 'xyz');
```
## 绑定打印员
若需要使用微信打单 PC 软件,才需要调用。
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.updatePrinter.html
```php
$app->express->bindPrinter($openid);
```
## 解绑打印员
若需要使用微信打单 PC 软件,才需要调用。
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/express/by-business/logistics.updatePrinter.html
```php
$app->express->unbindPrinter($openid);
```

View File

@@ -0,0 +1,24 @@
# 小程序
```php
use EasyWeChat\Factory;
$config = [
'app_id' => 'wx3cf0f39249eb0exx',
'secret' => 'f1c242f4f28f735d4687abb469072axx',
// 下面为可选项
// 指定 API 调用返回结果的类型array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => __DIR__.'/wechat.log',
],
];
$app = Factory::miniProgram($config);
```
`$app` 在所有相关小程序的文档都是指 `Factory::miniProgram` 得到的实例,就不在每个页面单独写了。

View File

@@ -0,0 +1,68 @@
# 附近的小程序
> 微信文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/nearby-poi/nearbyPoi.add.html
## 添加地点
```php
$params = [
'kf_info' => '{"open_kf":true,"kf_headimg":"http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg","kf_name":"Harden"}',
'pic_list' => '{"list":["http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg","http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITRneE5FS9uYruXGMmrtmhsBySwddEWUGOibG8Ze2NT5E3Dyt79I0htNg/0?wx_fmt=jpeg"]}',
'service_infos' => '{"service_infos":[{"id":2,"type":1,"name":"快递","appid":"wx1373169e494e0c39","path":"index"},{"id":0,"type":2,"name":"自定义","appid":"wx1373169e494e0c39","path":"index"}]}',
'store_name' => '羊村小马烧烤',
'contract_phone' => '111111111',
'hour' => '00:00-11:11',
'company_name' => '深圳市腾讯计算机系统有限公司',
'credential' => '156718193518281',
'address' => '新疆维吾尔自治区克拉玛依市克拉玛依区碧水路15-1-8号(碧水云天广场)',
'qualification_list' => '3LaLzqiTrQcD20DlX_o-OV1-nlYMu7sdVAL7SV2PrxVyjZFZZmB3O6LPGaYXlZWq',
];
$app->nearby_poi->add($params);
```
## 更新地点
```php
$poiId = 'xxxxxxxx';
$params = [
'kf_info' => '{"open_kf":true,"kf_headimg":"http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg","kf_name":"Harden"}',
'pic_list' => '{"list":["http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITqmP914zSwhajIEJzUPpx40P7R8fRe1QmicneQMhFzpZNhSLjrvU1pIA/0?wx_fmt=jpeg","http://mmbiz.qpic.cn/mmbiz_jpg/kKMgNtnEfQzDKpLXYhgo3W3Gndl34gITRneE5FS9uYruXGMmrtmhsBySwddEWUGOibG8Ze2NT5E3Dyt79I0htNg/0?wx_fmt=jpeg"]}',
'service_infos' => '{"service_infos":[{"id":2,"type":1,"name":"快递","appid":"wx1373169e494e0c39","path":"index"},{"id":0,"type":2,"name":"自定义","appid":"wx1373169e494e0c39","path":"index"}]}',
'contract_phone' => '111111111',
'hour' => '00:00-11:11',
'company_name' => '深圳市腾讯计算机系统有限公司',
'credential' => '156718193518281',
'address' => '新疆维吾尔自治区克拉玛依市克拉玛依区碧水路15-1-8号(碧水云天广场)',
'qualification_list' => '3LaLzqiTrQcD20DlX_o-OV1-nlYMu7sdVAL7SV2PrxVyjZFZZmB3O6LPGaYXlZWq',
];
$app->nearby_poi->update($poiId, $params);
```
## 删除地点
```php
$poiId = 'xxxxxxxx';
$app->nearby_poi->delete($poiId);
```
## 地点列表
```php
$page = 1;
$pageRows = 10;
$app->nearby_poi->list($page, $pageRows);
```
## 设置地点展示状态
```php
$poiId = 'xxxxxxxx';
$status = 0; // 0: 不展示1展示
$app->nearby_poi->setVisibility($poiId, $status);
```

View File

@@ -0,0 +1,54 @@
# 插件管理
> 微信文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/plugin-management/pluginManager.applyPlugin.html
## 申请使用插件
```php
$pluginAppId = 'xxxxxxxxx';
$app->plugin->apply($pluginAppId);
```
## 删除已添加的插件
```php
$pluginAppId = 'xxxxxxxxx';
$app->plugin->unbind($pluginAppId);
```
## 查询已添加的插件
```php
$app->plugin->list();
```
## 获取当前所有插件使用方
```php
$page = 1;
$size = 10;
$app->plugin_dev->getUsers($page, $size);
```
## 同意插件使用申请
```php
$appId = 'wxxxxxxxxxxxxxx';
$app->plugin_dev->agree($appId);
```
## 拒绝插件使用申请
```php
$app->plugin_dev->refuse('拒绝理由');
```
## 删除已拒绝的申请者
```php
$app->plugin_dev->delete();
```

View File

@@ -0,0 +1,22 @@
# 生物认证
## 生物认证秘钥签名验证
> https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/soter/soter.verifySignature.html
```php
$app->soter->verifySignature($openid, $json, $signature);
```
返回值示例:
```json
{
"is_ok": true
}
```
参数说明:
> - string $openid - 用户 openid
> - string $json - 通过 [wx.startSoterAuthentication](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/soter/wx.startSoterAuthentication.html) 成功回调获得的 resultJSON 字段
> - string $signature - 通过 [wx.startSoterAuthentication](https://developers.weixin.qq.com/miniprogram/dev/api/open-api/soter/wx.startSoterAuthentication.html) 成功回调获得的 resultJSONSignature 字段

View File

@@ -0,0 +1,71 @@
# 订阅消息
> 微信文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/subscribe-message/subscribeMessage.addTemplate.html
## 组合模板并添加至帐号下的个人模板库
```php
$tid = 563; // 模板标题 id可通过接口获取也可登录小程序后台查看获取
$kidList = [1, 2]; // 开发者自行组合好的模板关键词列表,可以通过 `getTemplateKeywords` 方法获取
$sceneDesc = '提示用户图书到期'; // 服务场景描述,非必填
$app->subscribe_message->addTemplate($tid, $kidList, $sceneDesc);
```
## 删除帐号下的个人模板
```php
$templateId = 'bDmywsp2oEHjwAadTGKkUHpC0RgBVPvfAM7Cu1s03z8';
$app->subscribe_message->deleteTemplate($templateId);
```
## 获取小程序账号的类目
```php
$app->subscribe_message->getCategory();
```
## 获取模板标题的关键词列表
```php
$tid = 563; // 模板标题 id可通过接口获取也可登录小程序后台查看获取
$app->subscribe_message->getTemplateKeywords($tid);
```
## 获取帐号所属类目下的公共模板标题
```php
$ids = [612, 613]; // 类目 id
$start = 0; // 用于分页,表示从 start 开始。从 0 开始计数。
$limit = 30; // 用于分页,表示拉取 limit 条记录。最大为 30。
$app->subscribe_message->getTemplateTitles($ids, $start, $limit);
```
## 获取当前帐号下的个人模板列表
```php
$app->subscribe_message->getTemplates();
```
## 发送订阅消息
```php
$data = [
'template_id' => 'bDmywsp2oEHjwAadTGKkUJ-eJEiMiOf7H-dZ7wjdw80', // 所需下发的订阅模板id
'touser' => 'oSyZp5OBNPBRhG-7BVgWxbiNZm', // 接收者(用户)的 openid
'page' => '', // 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,示例index?foo=bar。该字段不填则模板无跳转。
'data' => [ // 模板内容,格式形如 { "key1": { "value": any }, "key2": { "value": any } }
'date01' => [
'value' => '2019-12-01',
],
'number01' => [
'value' => 10,
],
],
];
$app->subscribe_message->send($data);
```

View File

@@ -0,0 +1,47 @@
# 模板消息
## 获取小程序模板库标题列表
```
$app->template_message->list($offset, $count);
```
## 获取模板库某个模板标题下关键词库
```
$app->template_message->get($id);
```
## 组合模板并添加至帐号下的个人模板库
```
$app->template_message->add($id, $keywordIdList);
```
## 获取帐号下已存在的模板列表
```
$app->template_message->getTemplates($offset, $count);
```
## 删除帐号下的某个模板
```
$app->template_message->delete($templateId);
```
## 发送模板消息
```php
$app->template_message->send([
'touser' => 'user-openid',
'template_id' => 'template-id',
'page' => 'index',
'form_id' => 'form-id',
'data' => [
'keyword1' => 'VALUE',
'keyword2' => 'VALUE2',
// ...
],
]);
```

View File

@@ -0,0 +1,4 @@
# 其它
### 其它

View File

@@ -0,0 +1,18 @@
# 多账号接入
如果你想使用本项目接入多个公众号,在本程序中,您可以为每个帐号都设置一个 id此 id 对应了该帐号的 appid、token 等信息。
如下表
| id | appId | secret | 其它... |
| ---- | -------------------- | ---------------------------------- | ----- |
| 1 | `wx3cf0f39249eb0e60` | `f28f735d4f1c242f4687abb469072a29` | ... |
| 2 | `wx49eb0e63cf0f39s2` | `8f735d4687abb469f1c2422a29f4f207` | ... |
| N | `wx5cfeb0e60f392490` | `35f8f27d46f1c242f487a9072a29bb46` | ... |
在微信公众平台的设置中,您可以将您帐号中平台的 `url` 设置为 `您的网址/?id=xxx`,如:
```
http://www.easywechat.com/wechat?id=1
```
而在程序入口处,根据 `id` 查找对应帐号的 `appid` 和 其它信息来创建配置数组创建实例即可。

Some files were not shown because too many files have changed in this diff Show More